Hi all,
Bunch more bugfixes (intel-gfx-ci wasn't fully happy yet, and justifiedly so) all over, bunch more acks/reviews.
Still need a lot more review and acks. Recommended reading order is still to start with the doc patch at the end for the big picture, then the code changes in sequence.
Also cc imx folks, they've realized that devm_kzalloc isn't a great idea the hard way.
Cheers, Daniel
Daniel Vetter (51): mm/sl[uo]b: export __kmalloc_track(_node)_caller drm/i915: Don't clear drvdata in ->release drm: add managed resources tied to drm_device drm: Set final_kfree in drm_dev_alloc drm/mipi_dbi: Use drmm_add_final_kfree in all drivers drm/udl: Use drmm_add_final_kfree drm/qxl: Use drmm_add_final_kfree drm/i915: Use drmm_add_final_kfree drm/cirrus: Use drmm_add_final_kfree drm/v3d: Use drmm_add_final_kfree drm/tidss: Use drmm_add_final_kfree drm/mcde: Use drmm_add_final_kfree drm/vgem: Use drmm_add_final_kfree drm/vkms: Use drmm_add_final_kfree drm/repaper: Use drmm_add_final_kfree drm/inigenic: Use drmm_add_final_kfree drm/gm12u320: Use drmm_add_final_kfree drm/<drivers>: Use drmm_add_final_kfree drm: Cleanups after drmm_add_final_kfree rollout drm: Handle dev->unique with drmm_ drm: Use drmm_ for drm_dev_init cleanup drm: manage drm_minor cleanup with drmm_ drm: Manage drm_gem_init with drmm_ drm: Manage drm_vblank_cleanup with drmm_ drm: Garbage collect drm_dev_fini drm: Manage drm_mode_config_init with drmm_ drm/bochs: Remove leftover drm_atomic_helper_shutdown drm/bochs: Drop explicit drm_mode_config_cleanup drm/cirrus: Drop explicit drm_mode_config_cleanup call drm/cirrus: Fully embrace devm_ drm/ingenic: Drop explicit drm_mode_config_cleanup call drm/mcde: Drop explicit drm_mode_config_cleanup call drm/mcde: More devm_drm_dev_init drm/meson: Drop explicit drm_mode_config_cleanup call drm/pl111: Drop explicit drm_mode_config_cleanup call drm/rcar-du: Drop explicit drm_mode_config_cleanup call drm/rockchip: Drop explicit drm_mode_config_cleanup call drm/stm: Drop explicit drm_mode_config_cleanup call drm/shmob: Drop explicit drm_mode_config_cleanup call drm/mtk: Drop explicit drm_mode_config_cleanup call drm/tidss: Drop explicit drm_mode_config_cleanup call drm/gm12u320: More drmm_ drm/gm12u320: Use devm_drm_dev_init drm/gm12u320: Use helpers for shutdown/suspend/resume drm/gm12u320: Simplify upload work drm/repaper: Drop explicit drm_mode_config_cleanup call drm/mipi-dbi: Move drm_mode_config_init into mipi library drm/mipi-dbi: Drop explicit drm_mode_config_cleanup call drm/udl: Drop explicit drm_mode_config_cleanup call drm/udl: drop drm_driver.release hook drm: Add docs for managed resources
Documentation/gpu/drm-internals.rst | 12 + drivers/gpu/drm/Makefile | 3 +- .../gpu/drm/arm/display/komeda/komeda_kms.c | 2 + drivers/gpu/drm/armada/armada_drv.c | 2 + drivers/gpu/drm/bochs/bochs.h | 1 - drivers/gpu/drm/bochs/bochs_drv.c | 6 +- drivers/gpu/drm/bochs/bochs_kms.c | 15 +- drivers/gpu/drm/cirrus/cirrus.c | 74 ++--- drivers/gpu/drm/drm_drv.c | 219 ++++++------- drivers/gpu/drm/drm_gem.c | 21 +- drivers/gpu/drm/drm_internal.h | 5 +- drivers/gpu/drm/drm_managed.c | 294 ++++++++++++++++++ drivers/gpu/drm/drm_mipi_dbi.c | 24 +- drivers/gpu/drm/drm_mode_config.c | 13 +- drivers/gpu/drm/drm_vblank.c | 31 +- drivers/gpu/drm/i915/i915_drv.c | 22 +- drivers/gpu/drm/i915/i915_drv.h | 3 + .../gpu/drm/i915/selftests/mock_gem_device.c | 33 +- drivers/gpu/drm/ingenic/ingenic-drm.c | 17 +- drivers/gpu/drm/mcde/mcde_drv.c | 35 +-- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 9 +- drivers/gpu/drm/meson/meson_drv.c | 5 +- drivers/gpu/drm/pl111/pl111_drv.c | 12 +- drivers/gpu/drm/qxl/qxl_drv.c | 2 - drivers/gpu/drm/qxl/qxl_kms.c | 2 + drivers/gpu/drm/rcar-du/rcar_du_drv.c | 1 - drivers/gpu/drm/rcar-du/rcar_du_kms.c | 4 +- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 12 +- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 2 - drivers/gpu/drm/shmobile/shmob_drm_kms.c | 6 +- drivers/gpu/drm/stm/drv.c | 10 +- drivers/gpu/drm/tidss/tidss_drv.c | 10 +- drivers/gpu/drm/tidss/tidss_kms.c | 19 +- drivers/gpu/drm/tidss/tidss_kms.h | 1 - drivers/gpu/drm/tiny/gm12u320.c | 226 +++++--------- drivers/gpu/drm/tiny/hx8357d.c | 5 +- drivers/gpu/drm/tiny/ili9225.c | 5 +- drivers/gpu/drm/tiny/ili9341.c | 5 +- drivers/gpu/drm/tiny/ili9486.c | 5 +- drivers/gpu/drm/tiny/mi0283qt.c | 5 +- drivers/gpu/drm/tiny/repaper.c | 14 +- drivers/gpu/drm/tiny/st7586.c | 5 +- drivers/gpu/drm/tiny/st7735r.c | 5 +- drivers/gpu/drm/udl/udl_drv.c | 14 +- drivers/gpu/drm/udl/udl_drv.h | 2 - drivers/gpu/drm/udl/udl_main.c | 10 - drivers/gpu/drm/udl/udl_modeset.c | 21 +- drivers/gpu/drm/v3d/v3d_drv.c | 38 +-- drivers/gpu/drm/vboxvideo/vbox_drv.c | 2 + drivers/gpu/drm/vgem/vgem_drv.c | 15 +- drivers/gpu/drm/vkms/vkms_drv.c | 19 +- drivers/gpu/drm/xen/xen_drm_front.c | 4 +- include/drm/drm_device.h | 12 + include/drm/drm_drv.h | 9 +- include/drm/drm_managed.h | 116 +++++++ include/drm/drm_mipi_dbi.h | 1 - include/drm/drm_mode_config.h | 2 +- include/drm/drm_print.h | 6 + mm/slob.c | 2 + mm/slub.c | 2 + 60 files changed, 868 insertions(+), 609 deletions(-) create mode 100644 drivers/gpu/drm/drm_managed.c create mode 100644 include/drm/drm_managed.h
slab does this already, and I want to use this in a memory allocation tracker in drm for stuff that's tied to the lifetime of a drm_device, not the underlying struct device. Kinda like devres, but for drm.
Acked-by: Andrew Morton akpm@linux-foundation.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Christoph Lameter cl@linux.com Cc: Pekka Enberg penberg@kernel.org Cc: David Rientjes rientjes@google.com Cc: Joonsoo Kim iamjoonsoo.kim@lge.com Cc: Andrew Morton akpm@linux-foundation.org Cc: linux-mm@kvack.org -- I plan to merge this through drm-misc-next (with Andrew's ack) once the remainder of the drm series is in shape. -Daniel --- mm/slob.c | 2 ++ mm/slub.c | 2 ++ 2 files changed, 4 insertions(+)
diff --git a/mm/slob.c b/mm/slob.c index fa53e9f73893..ac2aecfbc7a8 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -524,6 +524,7 @@ void *__kmalloc_track_caller(size_t size, gfp_t gfp, unsigned long caller) { return __do_kmalloc_node(size, gfp, NUMA_NO_NODE, caller); } +EXPORT_SYMBOL(__kmalloc_track_caller);
#ifdef CONFIG_NUMA void *__kmalloc_node_track_caller(size_t size, gfp_t gfp, @@ -531,6 +532,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfp, { return __do_kmalloc_node(size, gfp, node, caller); } +EXPORT_SYMBOL(__kmalloc_node_track_caller); #endif
void kfree(const void *block) diff --git a/mm/slub.c b/mm/slub.c index be2854b5b1c9..7271fb235ed8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4358,6 +4358,7 @@ void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, unsigned long caller)
return ret; } +EXPORT_SYMBOL(__kmalloc_track_caller);
#ifdef CONFIG_NUMA void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags, @@ -4388,6 +4389,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags,
return ret; } +EXPORT_SYMBOL(__kmalloc_node_track_caller); #endif
#ifdef CONFIG_SYSFS
For two reasons:
- The driver core clears this already for us after we're unloaded in __device_release_driver().
- It's way too late, the drm_device ->release callback might massively outlive the underlying physical device, since a drm_device can't be kept alive by open drm_file or well really anything else userspace is still hanging onto. So if we clear this ourselves, we should clear it in the pci ->remove callback, not in the drm_device ->relase callback.
Looking at git history this was fixed in the driver core with
commit 0998d0631001288a5974afc0b2a5f568bcdecb4d Author: Hans de Goede hdegoede@redhat.com Date: Wed May 23 00:09:34 2012 +0200
device-core: Ensure drvdata = NULL when no driver is bound
v2: Cite the core fix in the commit message (Chris).
Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: Chris Wilson chris@chris-wilson.co.uk Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/i915/i915_drv.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index b086132df1b7..0b59a9bd2581 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -913,9 +913,6 @@ static void i915_driver_destroy(struct drm_i915_private *i915)
drm_dev_fini(&i915->drm); kfree(i915); - - /* And make sure we never chase our dangling pointer from pci_dev */ - pci_set_drvdata(pdev, NULL); }
/**
We have lots of these. And the cleanup code tends to be of dubious quality. The biggest wrong pattern is that developers use devm_, which ties the release action to the underlying struct device, whereas all the userspace visible stuff attached to a drm_device can long outlive that one (e.g. after a hotunplug while userspace has open files and mmap'ed buffers). Give people what they want, but with more correctness.
Mostly copied from devres.c, with types adjusted to fit drm_device and a few simplifications - I didn't (yet) copy over everything. Since the types don't match code sharing looked like a hopeless endeavour.
For now it's only super simplified, no groups, you can't remove actions (but kfree exists, we'll need that soon). Plus all specific to drm_device ofc, including the logging. Which I didn't bother to make compile-time optional, since none of the other drm logging is compile time optional either.
One tricky bit here is the chicken&egg between allocating your drm_device structure and initiliazing it with drm_dev_init. For perfect onion unwinding we'd need to have the action to kfree the allocation registered before drm_dev_init registers any of its own release handlers. But drm_dev_init doesn't know where exactly the drm_device is emebedded into the overall structure, and by the time it returns it'll all be too late. And forcing drivers to be able clean up everything except the one kzalloc is silly.
Work around this by having a very special final_kfree pointer. This also avoids troubles with the list head possibly disappearing from underneath us when we release all resources attached to the drm_device.
v2: Do all the kerneldoc at the end, to avoid lots of fairly pointless shuffling while getting everything into shape.
v3: Add static to add/del_dr (Neil) Move typo fix to the right patch (Neil)
v4: Enforce contract for drmm_add_final_kfree:
Use ksize() to check that the drm_device is indeed contained somewhere in the final kfree(). Because we need that or the entire managed release logic blows up in a pile of use-after-frees. Motivated by a discussion with Laurent.
v5: Review from Laurent: - %zu instead of casting size_t - header guards - sorting of includes - guarding of data assignment if we didn't allocate it for a NULL pointer - delete spurious newline - cast void* data parameter correctly in ->release call, no idea how this even worked before
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Neil Armstrong <narmstrong@baylibre.com Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: "Rafael J. Wysocki" rafael@kernel.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- Documentation/gpu/drm-internals.rst | 6 + drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_drv.c | 13 ++- drivers/gpu/drm/drm_internal.h | 3 + drivers/gpu/drm/drm_managed.c | 175 ++++++++++++++++++++++++++++ include/drm/drm_device.h | 12 ++ include/drm/drm_managed.h | 30 +++++ include/drm/drm_print.h | 6 + 8 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/drm_managed.c create mode 100644 include/drm/drm_managed.h
diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index a73320576ca9..a6b6145fda78 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -132,6 +132,12 @@ be unmapped; on many devices, the ROM address decoder is shared with other BARs, so leaving it mapped could cause undesired behaviour like hangs or memory corruption.
+Managed Resources +----------------- + +.. kernel-doc:: drivers/gpu/drm/drm_managed.c + :doc: managed resources + Bus-specific Device Registration and PCI Support ------------------------------------------------
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7f72ef5e7811..183c60048307 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,8 @@ drm-y := drm_auth.o drm_cache.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ - drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o + drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \ + drm_managed.o
drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9fcd6ab3c154..3e5627d6eba6 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -629,6 +629,9 @@ int drm_dev_init(struct drm_device *dev, dev->dev = get_device(parent); dev->driver = driver;
+ INIT_LIST_HEAD(&dev->managed.resources); + spin_lock_init(&dev->managed.lock); + /* no per-device feature limits by default */ dev->driver_features = ~0u;
@@ -828,8 +831,16 @@ static void drm_dev_release(struct kref *ref) dev->driver->release(dev); } else { drm_dev_fini(dev); - kfree(dev); + if (!dev->managed.final_kfree) { + WARN_ON(!list_empty(&dev->managed.resources)); + kfree(dev); + } } + + drm_managed_release(dev); + + if (dev->managed.final_kfree) + kfree(dev->managed.final_kfree); }
/** diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index aeec2e68d772..8c2628dfc6c7 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -89,6 +89,9 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr struct drm_minor *drm_minor_acquire(unsigned int minor_id); void drm_minor_release(struct drm_minor *minor);
+/* drm_managed.c */ +void drm_managed_release(struct drm_device *dev); + /* drm_vblank.c */ void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); void drm_vblank_cleanup(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c new file mode 100644 index 000000000000..a36d4604ee18 --- /dev/null +++ b/drivers/gpu/drm/drm_managed.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Intel + * + * Based on drivers/base/devres.c + */ + +#include <drm/drm_managed.h> + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <drm/drm_device.h> +#include <drm/drm_print.h> + +/** + * DOC: managed resources + * + * Inspired by struct &device managed resources, but tied to the lifetime of + * struct &drm_device, which can outlive the underlying physical device, usually + * when userspace has some open files and other handles to resources still open. + */ +struct drmres_node { + struct list_head entry; + drmres_release_t release; + const char *name; + size_t size; +}; + +struct drmres { + struct drmres_node node; + /* + * Some archs want to perform DMA into kmalloc caches + * and need a guaranteed alignment larger than + * the alignment of a 64-bit integer. + * Thus we use ARCH_KMALLOC_MINALIGN here and get exactly the same + * buffer alignment as if it was allocated by plain kmalloc(). + */ + u8 __aligned(ARCH_KMALLOC_MINALIGN) data[]; +}; + +void drm_managed_release(struct drm_device *dev) +{ + struct drmres *dr, *tmp; + + drm_dbg_drmres(dev, "drmres release begin\n"); + list_for_each_entry_safe(dr, tmp, &dev->managed.resources, node.entry) { + drm_dbg_drmres(dev, "REL %p %s (%zu bytes)\n", + dr, dr->node.name, dr->node.size); + + if (dr->node.release) + dr->node.release(dev, dr->node.size ? *(void **)&dr->data : NULL); + + list_del(&dr->node.entry); + kfree(dr); + } + drm_dbg_drmres(dev, "drmres release end\n"); +} + +static __always_inline struct drmres * alloc_dr(drmres_release_t release, + size_t size, gfp_t gfp, int nid) +{ + size_t tot_size; + struct drmres *dr; + + /* We must catch any near-SIZE_MAX cases that could overflow. */ + if (unlikely(check_add_overflow(sizeof(*dr), size, &tot_size))) + return NULL; + + dr = kmalloc_node_track_caller(tot_size, gfp, nid); + if (unlikely(!dr)) + return NULL; + + memset(dr, 0, offsetof(struct drmres, data)); + + INIT_LIST_HEAD(&dr->node.entry); + dr->node.release = release; + dr->node.size = size; + + return dr; +} + +static void del_dr(struct drm_device *dev, struct drmres *dr) +{ + list_del_init(&dr->node.entry); + + drm_dbg_drmres(dev, "DEL %p %s (%lu bytes)\n", + dr, dr->node.name, (unsigned long) dr->node.size); +} + +static void add_dr(struct drm_device *dev, struct drmres *dr) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->managed.lock, flags); + list_add(&dr->node.entry, &dev->managed.resources); + spin_unlock_irqrestore(&dev->managed.lock, flags); + + drm_dbg_drmres(dev, "ADD %p %s (%lu bytes)\n", + dr, dr->node.name, (unsigned long) dr->node.size); +} + +void drmm_add_final_kfree(struct drm_device *dev, void *parent) +{ + WARN_ON(dev->managed.final_kfree); + WARN_ON(dev < (struct drm_device *) parent); + WARN_ON(dev + 1 >= (struct drm_device *) (parent + ksize(parent))); + dev->managed.final_kfree = parent; +} +EXPORT_SYMBOL(drmm_add_final_kfree); + +int __drmm_add_action(struct drm_device *dev, + drmres_release_t action, + void *data, const char *name) +{ + struct drmres *dr; + void **void_ptr; + + dr = alloc_dr(action, data ? sizeof(void*) : 0, + GFP_KERNEL | __GFP_ZERO, + dev_to_node(dev->dev)); + if (!dr) + return -ENOMEM; + dr->node.name = name; + if (data) { + void_ptr = (void **)&dr->data; + *void_ptr = data; + } + + add_dr(dev, dr); + + return 0; +} +EXPORT_SYMBOL(__drmm_add_action); + +void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) +{ + struct drmres *dr; + + dr = alloc_dr(NULL, size, gfp, dev_to_node(dev->dev)); + if (!dr) + return NULL; + dr->node.name = "kmalloc"; + + add_dr(dev, dr); + + return dr->data; +} +EXPORT_SYMBOL(drmm_kmalloc); + +void drmm_kfree(struct drm_device *dev, void *data) +{ + struct drmres *dr_match = NULL, *dr; + unsigned long flags; + + if (!data) + return; + + spin_lock_irqsave(&dev->managed.lock, flags); + list_for_each_entry(dr, &dev->managed.resources, node.entry) { + if (dr->data == data) { + dr_match = dr; + del_dr(dev, dr_match); + break; + } + } + spin_unlock_irqrestore(&dev->managed.lock, flags); + + if (WARN_ON(!dr_match)) + return; + + kfree(dr_match); +} +EXPORT_SYMBOL(drmm_kfree); diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index bb60a949f416..2790c9ed614e 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -67,6 +67,18 @@ struct drm_device { /** @dev: Device structure of bus-device */ struct device *dev;
+ /** + * @managed: + * + * Managed resources linked to the lifetime of this &drm_device as + * tracked by @ref. + */ + struct { + struct list_head resources; + void *final_kfree; + spinlock_t lock; + } managed; + /** @driver: DRM driver managing the device */ struct drm_driver *driver;
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h new file mode 100644 index 000000000000..7b5df7d09b19 --- /dev/null +++ b/include/drm/drm_managed.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef _DRM_MANAGED_H_ +#define _DRM_MANAGED_H_ + +#include <linux/gfp.h> +#include <linux/types.h> + +struct drm_device; + +typedef void (*drmres_release_t)(struct drm_device *dev, void *res); + +#define drmm_add_action(dev, action, data) \ + __drmm_add_action(dev, action, data, #action) + +int __must_check __drmm_add_action(struct drm_device *dev, + drmres_release_t action, + void *data, const char *name); + +void drmm_add_final_kfree(struct drm_device *dev, void *parent); + +void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc; +static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp) +{ + return drmm_kmalloc(dev, size, gfp | __GFP_ZERO); +} + +void drmm_kfree(struct drm_device *dev, void *data); + +#endif diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index ca7cee8e728a..1c9417430d08 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -313,6 +313,10 @@ enum drm_debug_category { * @DRM_UT_DP: Used in the DP code. */ DRM_UT_DP = 0x100, + /** + * @DRM_UT_DRMRES: Used in the drm managed resources code. + */ + DRM_UT_DRMRES = 0x200, };
static inline bool drm_debug_enabled(enum drm_debug_category category) @@ -442,6 +446,8 @@ void drm_dev_dbg(const struct device *dev, enum drm_debug_category category, drm_dev_dbg((drm)->dev, DRM_UT_LEASE, fmt, ##__VA_ARGS__) #define drm_dbg_dp(drm, fmt, ...) \ drm_dev_dbg((drm)->dev, DRM_UT_DP, fmt, ##__VA_ARGS__) +#define drm_dbg_drmres(drm, fmt, ...) \ + drm_dev_dbg((drm)->dev, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
/*
Hi Daniel.
Some nitpicks / bikeshedding below.
Sam
On Thu, Feb 27, 2020 at 07:14:34PM +0100, Daniel Vetter wrote:
We have lots of these. And the cleanup code tends to be of dubious quality. The biggest wrong pattern is that developers use devm_, which ties the release action to the underlying struct device, whereas all the userspace visible stuff attached to a drm_device can long outlive that one (e.g. after a hotunplug while userspace has open files and mmap'ed buffers). Give people what they want, but with more correctness.
Mostly copied from devres.c, with types adjusted to fit drm_device and a few simplifications - I didn't (yet) copy over everything. Since the types don't match code sharing looked like a hopeless endeavour.
Readability had been increased if the short names was not reused.
s/dr_/drmres_/
But I know, this is in the bikeshedding area.
For now it's only super simplified, no groups, you can't remove actions (but kfree exists, we'll need that soon). Plus all specific to drm_device ofc, including the logging. Which I didn't bother to make compile-time optional, since none of the other drm logging is compile time optional either.
One tricky bit here is the chicken&egg between allocating your drm_device structure and initiliazing it with drm_dev_init. For perfect onion unwinding we'd need to have the action to kfree the allocation registered before drm_dev_init registers any of its own release handlers. But drm_dev_init doesn't know where exactly the drm_device is emebedded into the overall structure, and by the time it returns it'll all be too late. And forcing drivers to be able clean up everything except the one kzalloc is silly.
Work around this by having a very special final_kfree pointer. This also avoids troubles with the list head possibly disappearing from underneath us when we release all resources attached to the drm_device.
v2: Do all the kerneldoc at the end, to avoid lots of fairly pointless shuffling while getting everything into shape.
v3: Add static to add/del_dr (Neil) Move typo fix to the right patch (Neil)
v4: Enforce contract for drmm_add_final_kfree:
Use ksize() to check that the drm_device is indeed contained somewhere in the final kfree(). Because we need that or the entire managed release logic blows up in a pile of use-after-frees. Motivated by a discussion with Laurent.
v5: Review from Laurent:
- %zu instead of casting size_t
- header guards
- sorting of includes
- guarding of data assignment if we didn't allocate it for a NULL pointer
- delete spurious newline
- cast void* data parameter correctly in ->release call, no idea how this even worked before
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Neil Armstrong <narmstrong@baylibre.com Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: "Rafael J. Wysocki" rafael@kernel.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
Documentation/gpu/drm-internals.rst | 6 + drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_drv.c | 13 ++- drivers/gpu/drm/drm_internal.h | 3 + drivers/gpu/drm/drm_managed.c | 175 ++++++++++++++++++++++++++++ include/drm/drm_device.h | 12 ++ include/drm/drm_managed.h | 30 +++++ include/drm/drm_print.h | 6 + 8 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/drm_managed.c create mode 100644 include/drm/drm_managed.h
diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index a73320576ca9..a6b6145fda78 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -132,6 +132,12 @@ be unmapped; on many devices, the ROM address decoder is shared with other BARs, so leaving it mapped could cause undesired behaviour like hangs or memory corruption.
+Managed Resources +-----------------
+.. kernel-doc:: drivers/gpu/drm/drm_managed.c
- :doc: managed resources
Bus-specific Device Registration and PCI Support
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7f72ef5e7811..183c60048307 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,8 @@ drm-y := drm_auth.o drm_cache.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \
drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o
drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \
drm_managed.o
drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9fcd6ab3c154..3e5627d6eba6 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -629,6 +629,9 @@ int drm_dev_init(struct drm_device *dev, dev->dev = get_device(parent); dev->driver = driver;
- INIT_LIST_HEAD(&dev->managed.resources);
- spin_lock_init(&dev->managed.lock);
- /* no per-device feature limits by default */ dev->driver_features = ~0u;
@@ -828,8 +831,16 @@ static void drm_dev_release(struct kref *ref) dev->driver->release(dev); } else { drm_dev_fini(dev);
kfree(dev);
if (!dev->managed.final_kfree) {
WARN_ON(!list_empty(&dev->managed.resources));
kfree(dev);
}
This looks sub-optimal. We cannot be sure a driver have used drmm_add_final_kfree() if it makes use of drmm_. So we may not WARN in all relavant cases. Also, we cannot expect all drivers that uses devmm_ to have managed to get rid of their ->release call-back. So the right thing looks to me like we should move it out to be unconditional. Se we will WARN_ON(!list_empty(&dev->managed.resources)) always.
It also looks like we do a kfree(dev); (inside {} ) And then we access kfree() in the call to drm_managed_relase(dev) right after.
}
- drm_managed_release(dev);
- if (dev->managed.final_kfree)
kfree(dev->managed.final_kfree);
}
/** diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index aeec2e68d772..8c2628dfc6c7 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -89,6 +89,9 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr struct drm_minor *drm_minor_acquire(unsigned int minor_id); void drm_minor_release(struct drm_minor *minor);
+/* drm_managed.c */ +void drm_managed_release(struct drm_device *dev);
/* drm_vblank.c */ void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); void drm_vblank_cleanup(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c new file mode 100644 index 000000000000..a36d4604ee18 --- /dev/null +++ b/drivers/gpu/drm/drm_managed.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2020 Intel
- Based on drivers/base/devres.c
- */
+#include <drm/drm_managed.h>
+#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h>
+#include <drm/drm_device.h> +#include <drm/drm_print.h>
It is good practice to group the include files. And drm/ comes after linux/
+/**
- DOC: managed resources
- Inspired by struct &device managed resources, but tied to the lifetime of
- struct &drm_device, which can outlive the underlying physical device, usually
- when userspace has some open files and other handles to resources still open.
- */
+struct drmres_node {
- struct list_head entry;
- drmres_release_t release;
- const char *name;
- size_t size;
+};
Excessing indent? (I know, copied from somewhere else, but...)
+struct drmres {
- struct drmres_node node;
- /*
* Some archs want to perform DMA into kmalloc caches
* and need a guaranteed alignment larger than
* the alignment of a 64-bit integer.
* Thus we use ARCH_KMALLOC_MINALIGN here and get exactly the same
* buffer alignment as if it was allocated by plain kmalloc().
*/
- u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
+};
+void drm_managed_release(struct drm_device *dev) +{
- struct drmres *dr, *tmp;
- drm_dbg_drmres(dev, "drmres release begin\n");
- list_for_each_entry_safe(dr, tmp, &dev->managed.resources, node.entry) {
drm_dbg_drmres(dev, "REL %p %s (%zu bytes)\n",
dr, dr->node.name, dr->node.size);
if (dr->node.release)
dr->node.release(dev, dr->node.size ? *(void **)&dr->data : NULL);
list_del(&dr->node.entry);
kfree(dr);
- }
- drm_dbg_drmres(dev, "drmres release end\n");
+}
+static __always_inline struct drmres * alloc_dr(drmres_release_t release,
size_t size, gfp_t gfp, int nid)
Why do we force the compiler to inline this? Seems a little agressive.
All the two users so far uses dev_to_node(dev->dev) for the nid. Maybe let this function take a drm_device * and thus move the calculation to this function?
+{
- size_t tot_size;
- struct drmres *dr;
- /* We must catch any near-SIZE_MAX cases that could overflow. */
- if (unlikely(check_add_overflow(sizeof(*dr), size, &tot_size)))
return NULL;
- dr = kmalloc_node_track_caller(tot_size, gfp, nid);
- if (unlikely(!dr))
return NULL;
- memset(dr, 0, offsetof(struct drmres, data));
- INIT_LIST_HEAD(&dr->node.entry);
- dr->node.release = release;
- dr->node.size = size;
- return dr;
+}
+static void del_dr(struct drm_device *dev, struct drmres *dr) +{
- list_del_init(&dr->node.entry);
- drm_dbg_drmres(dev, "DEL %p %s (%lu bytes)\n",
dr, dr->node.name, (unsigned long) dr->node.size);
+}
+static void add_dr(struct drm_device *dev, struct drmres *dr) +{
- unsigned long flags;
- spin_lock_irqsave(&dev->managed.lock, flags);
- list_add(&dr->node.entry, &dev->managed.resources);
- spin_unlock_irqrestore(&dev->managed.lock, flags);
- drm_dbg_drmres(dev, "ADD %p %s (%lu bytes)\n",
dr, dr->node.name, (unsigned long) dr->node.size);
+}
+void drmm_add_final_kfree(struct drm_device *dev, void *parent) +{
- WARN_ON(dev->managed.final_kfree);
- WARN_ON(dev < (struct drm_device *) parent);
- WARN_ON(dev + 1 >= (struct drm_device *) (parent + ksize(parent)));
- dev->managed.final_kfree = parent;
+} +EXPORT_SYMBOL(drmm_add_final_kfree);
+int __drmm_add_action(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name)
+{
- struct drmres *dr;
- void **void_ptr;
- dr = alloc_dr(action, data ? sizeof(void*) : 0,
Hmm, data is an u8 pointer, not a void pointer. But this code is copied soo...
GFP_KERNEL | __GFP_ZERO,
So in this case the memset() in alloc_dr() is of no use. Anyway, it is zero bytes or a pointer size bytes. So no big deal I think.
dev_to_node(dev->dev));
- if (!dr)
return -ENOMEM;
Add empty line.
- dr->node.name = name;
- if (data) {
void_ptr = (void **)&dr->data;
*void_ptr = data;
- }
- add_dr(dev, dr);
- return 0;
+} +EXPORT_SYMBOL(__drmm_add_action);
See rant about __ named functions in another mail. drmm_add_action_named(..) would be better.
+void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) +{
- struct drmres *dr;
- dr = alloc_dr(NULL, size, gfp, dev_to_node(dev->dev));
- if (!dr)
return NULL;
- dr->node.name = "kmalloc";
- add_dr(dev, dr);
- return dr->data;
+} +EXPORT_SYMBOL(drmm_kmalloc);
+void drmm_kfree(struct drm_device *dev, void *data) +{
- struct drmres *dr_match = NULL, *dr;
- unsigned long flags;
- if (!data)
return;
- spin_lock_irqsave(&dev->managed.lock, flags);
- list_for_each_entry(dr, &dev->managed.resources, node.entry) {
if (dr->data == data) {
dr_match = dr;
del_dr(dev, dr_match);
break;
}
- }
- spin_unlock_irqrestore(&dev->managed.lock, flags);
- if (WARN_ON(!dr_match))
return;
- kfree(dr_match);
+} +EXPORT_SYMBOL(drmm_kfree); diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index bb60a949f416..2790c9ed614e 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -67,6 +67,18 @@ struct drm_device { /** @dev: Device structure of bus-device */ struct device *dev;
- /**
* @managed:
*
* Managed resources linked to the lifetime of this &drm_device as
* tracked by @ref.
*/
- struct {
struct list_head resources;
void *final_kfree;
spinlock_t lock;
- } managed;
I am missing kernel-doc here. At least document that lock is used to guard access to resources. (s/lock/lock_resources/ ?)
- /** @driver: DRM driver managing the device */ struct drm_driver *driver;
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h new file mode 100644 index 000000000000..7b5df7d09b19 --- /dev/null +++ b/include/drm/drm_managed.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0
+#ifndef _DRM_MANAGED_H_ +#define _DRM_MANAGED_H_
+#include <linux/gfp.h> +#include <linux/types.h>
+struct drm_device;
+typedef void (*drmres_release_t)(struct drm_device *dev, void *res);
+#define drmm_add_action(dev, action, data) \
- __drmm_add_action(dev, action, data, #action)
+int __must_check __drmm_add_action(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
+void drmm_add_final_kfree(struct drm_device *dev, void *parent);
+void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc; +static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp) +{
- return drmm_kmalloc(dev, size, gfp | __GFP_ZERO);
+}
+void drmm_kfree(struct drm_device *dev, void *data);
+#endif diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index ca7cee8e728a..1c9417430d08 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -313,6 +313,10 @@ enum drm_debug_category { * @DRM_UT_DP: Used in the DP code. */ DRM_UT_DP = 0x100,
- /**
* @DRM_UT_DRMRES: Used in the drm managed resources code.
*/
- DRM_UT_DRMRES = 0x200,
};
static inline bool drm_debug_enabled(enum drm_debug_category category) @@ -442,6 +446,8 @@ void drm_dev_dbg(const struct device *dev, enum drm_debug_category category, drm_dev_dbg((drm)->dev, DRM_UT_LEASE, fmt, ##__VA_ARGS__) #define drm_dbg_dp(drm, fmt, ...) \ drm_dev_dbg((drm)->dev, DRM_UT_DP, fmt, ##__VA_ARGS__) +#define drm_dbg_drmres(drm, fmt, ...) \
- drm_dev_dbg((drm)->dev, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
We use drmm for all the managed stuff. So drm_dbg_drmm() is for me a more logical name. This debug printing is used only in a few spots, so no big deal.
Sam
On Fri, Feb 28, 2020 at 11:45 PM Sam Ravnborg sam@ravnborg.org wrote:
Hi Daniel.
Some nitpicks / bikeshedding below.
Sam
On Thu, Feb 27, 2020 at 07:14:34PM +0100, Daniel Vetter wrote:
We have lots of these. And the cleanup code tends to be of dubious quality. The biggest wrong pattern is that developers use devm_, which ties the release action to the underlying struct device, whereas all the userspace visible stuff attached to a drm_device can long outlive that one (e.g. after a hotunplug while userspace has open files and mmap'ed buffers). Give people what they want, but with more correctness.
Mostly copied from devres.c, with types adjusted to fit drm_device and a few simplifications - I didn't (yet) copy over everything. Since the types don't match code sharing looked like a hopeless endeavour.
Readability had been increased if the short names was not reused.
s/dr_/drmres_/
But I know, this is in the bikeshedding area.
For now it's only super simplified, no groups, you can't remove actions (but kfree exists, we'll need that soon). Plus all specific to drm_device ofc, including the logging. Which I didn't bother to make compile-time optional, since none of the other drm logging is compile time optional either.
One tricky bit here is the chicken&egg between allocating your drm_device structure and initiliazing it with drm_dev_init. For perfect onion unwinding we'd need to have the action to kfree the allocation registered before drm_dev_init registers any of its own release handlers. But drm_dev_init doesn't know where exactly the drm_device is emebedded into the overall structure, and by the time it returns it'll all be too late. And forcing drivers to be able clean up everything except the one kzalloc is silly.
Work around this by having a very special final_kfree pointer. This also avoids troubles with the list head possibly disappearing from underneath us when we release all resources attached to the drm_device.
v2: Do all the kerneldoc at the end, to avoid lots of fairly pointless shuffling while getting everything into shape.
v3: Add static to add/del_dr (Neil) Move typo fix to the right patch (Neil)
v4: Enforce contract for drmm_add_final_kfree:
Use ksize() to check that the drm_device is indeed contained somewhere in the final kfree(). Because we need that or the entire managed release logic blows up in a pile of use-after-frees. Motivated by a discussion with Laurent.
v5: Review from Laurent:
- %zu instead of casting size_t
- header guards
- sorting of includes
- guarding of data assignment if we didn't allocate it for a NULL pointer
- delete spurious newline
- cast void* data parameter correctly in ->release call, no idea how this even worked before
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Neil Armstrong <narmstrong@baylibre.com Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: "Rafael J. Wysocki" rafael@kernel.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
Documentation/gpu/drm-internals.rst | 6 + drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_drv.c | 13 ++- drivers/gpu/drm/drm_internal.h | 3 + drivers/gpu/drm/drm_managed.c | 175 ++++++++++++++++++++++++++++ include/drm/drm_device.h | 12 ++ include/drm/drm_managed.h | 30 +++++ include/drm/drm_print.h | 6 + 8 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/drm_managed.c create mode 100644 include/drm/drm_managed.h
diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index a73320576ca9..a6b6145fda78 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -132,6 +132,12 @@ be unmapped; on many devices, the ROM address decoder is shared with other BARs, so leaving it mapped could cause undesired behaviour like hangs or memory corruption.
+Managed Resources +-----------------
+.. kernel-doc:: drivers/gpu/drm/drm_managed.c
- :doc: managed resources
Bus-specific Device Registration and PCI Support
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7f72ef5e7811..183c60048307 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,8 @@ drm-y := drm_auth.o drm_cache.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \
drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o
drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \
drm_managed.o
drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9fcd6ab3c154..3e5627d6eba6 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -629,6 +629,9 @@ int drm_dev_init(struct drm_device *dev, dev->dev = get_device(parent); dev->driver = driver;
INIT_LIST_HEAD(&dev->managed.resources);
spin_lock_init(&dev->managed.lock);
/* no per-device feature limits by default */ dev->driver_features = ~0u;
@@ -828,8 +831,16 @@ static void drm_dev_release(struct kref *ref) dev->driver->release(dev); } else { drm_dev_fini(dev);
kfree(dev);
if (!dev->managed.final_kfree) {
WARN_ON(!list_empty(&dev->managed.resources));
kfree(dev);
}
This looks sub-optimal. We cannot be sure a driver have used drmm_add_final_kfree() if it makes use of drmm_. So we may not WARN in all relavant cases. Also, we cannot expect all drivers that uses devmm_ to have managed to get rid of their ->release call-back.
The above is purely transition code. It gets cleaned up once all drivers call drmm_add_final_kfree(). This all disappears again, but indeed looks like the interim state isn't quite what we want.
So the right thing looks to me like we should move it out to be unconditional. Se we will WARN_ON(!list_empty(&dev->managed.resources)) always.
Until the driver has set drmm_add_final_kfree it's actually dangerous to use the drmm stuff. Exactly because of the use-after-free you point out below. Hence the warning to make sure there's no release actions. I'll shuffle this around to make sure we call kfree last for all possible paths and make sure this bisects all correctly.
It also looks like we do a kfree(dev); (inside {} ) And then we access kfree() in the call to drm_managed_relase(dev) right after.
}
drm_managed_release(dev);
if (dev->managed.final_kfree)
kfree(dev->managed.final_kfree);
}
/** diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index aeec2e68d772..8c2628dfc6c7 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -89,6 +89,9 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr struct drm_minor *drm_minor_acquire(unsigned int minor_id); void drm_minor_release(struct drm_minor *minor);
+/* drm_managed.c */ +void drm_managed_release(struct drm_device *dev);
/* drm_vblank.c */ void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); void drm_vblank_cleanup(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c new file mode 100644 index 000000000000..a36d4604ee18 --- /dev/null +++ b/drivers/gpu/drm/drm_managed.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) 2020 Intel
- Based on drivers/base/devres.c
- */
+#include <drm/drm_managed.h>
+#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h>
+#include <drm/drm_device.h> +#include <drm/drm_print.h>
It is good practice to group the include files. And drm/ comes after linux/
I try to put the main header first to make sure it's stand-alone, but I guess that works with the header check now? Do I need to do anything to get that checked?
+/**
- DOC: managed resources
- Inspired by struct &device managed resources, but tied to the lifetime of
- struct &drm_device, which can outlive the underlying physical device, usually
- when userspace has some open files and other handles to resources still open.
- */
+struct drmres_node {
struct list_head entry;
drmres_release_t release;
const char *name;
size_t size;
+};
Excessing indent? (I know, copied from somewhere else, but...)
+struct drmres {
struct drmres_node node;
/*
* Some archs want to perform DMA into kmalloc caches
* and need a guaranteed alignment larger than
* the alignment of a 64-bit integer.
* Thus we use ARCH_KMALLOC_MINALIGN here and get exactly the same
* buffer alignment as if it was allocated by plain kmalloc().
*/
u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
+};
+void drm_managed_release(struct drm_device *dev) +{
struct drmres *dr, *tmp;
drm_dbg_drmres(dev, "drmres release begin\n");
list_for_each_entry_safe(dr, tmp, &dev->managed.resources, node.entry) {
drm_dbg_drmres(dev, "REL %p %s (%zu bytes)\n",
dr, dr->node.name, dr->node.size);
if (dr->node.release)
dr->node.release(dev, dr->node.size ? *(void **)&dr->data : NULL);
list_del(&dr->node.entry);
kfree(dr);
}
drm_dbg_drmres(dev, "drmres release end\n");
+}
+static __always_inline struct drmres * alloc_dr(drmres_release_t release,
size_t size, gfp_t gfp, int nid)
Why do we force the compiler to inline this? Seems a little agressive.
It's not for performance, but for kmalloc_trace_caller. No point if our caller is always some boring function from drm_managed.c that calls alloc_dr. If we force alloc_dr to inline, then we get the caller of the drm_managed.c function traced as allocator. Much better.
(I stole that trick from devres.c)
I'll add a comment to explain this.
All the two users so far uses dev_to_node(dev->dev) for the nid. Maybe let this function take a drm_device * and thus move the calculation to this function?
Copypastes like that :-) I feel somewhat meh here ...
+{
size_t tot_size;
struct drmres *dr;
/* We must catch any near-SIZE_MAX cases that could overflow. */
if (unlikely(check_add_overflow(sizeof(*dr), size, &tot_size)))
return NULL;
dr = kmalloc_node_track_caller(tot_size, gfp, nid);
if (unlikely(!dr))
return NULL;
memset(dr, 0, offsetof(struct drmres, data));
INIT_LIST_HEAD(&dr->node.entry);
dr->node.release = release;
dr->node.size = size;
return dr;
+}
+static void del_dr(struct drm_device *dev, struct drmres *dr) +{
list_del_init(&dr->node.entry);
drm_dbg_drmres(dev, "DEL %p %s (%lu bytes)\n",
dr, dr->node.name, (unsigned long) dr->node.size);
+}
+static void add_dr(struct drm_device *dev, struct drmres *dr) +{
unsigned long flags;
spin_lock_irqsave(&dev->managed.lock, flags);
list_add(&dr->node.entry, &dev->managed.resources);
spin_unlock_irqrestore(&dev->managed.lock, flags);
drm_dbg_drmres(dev, "ADD %p %s (%lu bytes)\n",
dr, dr->node.name, (unsigned long) dr->node.size);
+}
+void drmm_add_final_kfree(struct drm_device *dev, void *parent) +{
WARN_ON(dev->managed.final_kfree);
WARN_ON(dev < (struct drm_device *) parent);
WARN_ON(dev + 1 >= (struct drm_device *) (parent + ksize(parent)));
dev->managed.final_kfree = parent;
+} +EXPORT_SYMBOL(drmm_add_final_kfree);
+int __drmm_add_action(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name)
+{
struct drmres *dr;
void **void_ptr;
dr = alloc_dr(action, data ? sizeof(void*) : 0,
Hmm, data is an u8 pointer, not a void pointer. But this code is copied soo...
GFP_KERNEL | __GFP_ZERO,
So in this case the memset() in alloc_dr() is of no use. Anyway, it is zero bytes or a pointer size bytes. So no big deal I think.
It's actually the drmm internal part of the allocation we're clearing. I guess we could make that conditional on __GFP_ZERO not being set, but it's just a cacheline so feels a bit silly. We're writing that cacheline anyway.
dev_to_node(dev->dev));
if (!dr)
return -ENOMEM;
Add empty line.
dr->node.name = name;
if (data) {
void_ptr = (void **)&dr->data;
*void_ptr = data;
}
add_dr(dev, dr);
return 0;
+} +EXPORT_SYMBOL(__drmm_add_action);
See rant about __ named functions in another mail. drmm_add_action_named(..) would be better.
+void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) +{
struct drmres *dr;
dr = alloc_dr(NULL, size, gfp, dev_to_node(dev->dev));
if (!dr)
return NULL;
dr->node.name = "kmalloc";
add_dr(dev, dr);
return dr->data;
+} +EXPORT_SYMBOL(drmm_kmalloc);
+void drmm_kfree(struct drm_device *dev, void *data) +{
struct drmres *dr_match = NULL, *dr;
unsigned long flags;
if (!data)
return;
spin_lock_irqsave(&dev->managed.lock, flags);
list_for_each_entry(dr, &dev->managed.resources, node.entry) {
if (dr->data == data) {
dr_match = dr;
del_dr(dev, dr_match);
break;
}
}
spin_unlock_irqrestore(&dev->managed.lock, flags);
if (WARN_ON(!dr_match))
return;
kfree(dr_match);
+} +EXPORT_SYMBOL(drmm_kfree); diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index bb60a949f416..2790c9ed614e 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -67,6 +67,18 @@ struct drm_device { /** @dev: Device structure of bus-device */ struct device *dev;
/**
* @managed:
*
* Managed resources linked to the lifetime of this &drm_device as
* tracked by @ref.
*/
struct {
struct list_head resources;
void *final_kfree;
spinlock_t lock;
} managed;
I am missing kernel-doc here. At least document that lock is used to guard access to resources. (s/lock/lock_resources/ ?)
Dunno why, but the support for name sub-structures seems to have broken in kerneldoc. So I can type it, but it's not showing up, so I didn't bother. Well I had it, but deleted it again. It's still documented to work, but I have no idea what I'm doing wrong.
/** @driver: DRM driver managing the device */ struct drm_driver *driver;
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h new file mode 100644 index 000000000000..7b5df7d09b19 --- /dev/null +++ b/include/drm/drm_managed.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0
+#ifndef _DRM_MANAGED_H_ +#define _DRM_MANAGED_H_
+#include <linux/gfp.h> +#include <linux/types.h>
+struct drm_device;
+typedef void (*drmres_release_t)(struct drm_device *dev, void *res);
+#define drmm_add_action(dev, action, data) \
__drmm_add_action(dev, action, data, #action)
+int __must_check __drmm_add_action(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
+void drmm_add_final_kfree(struct drm_device *dev, void *parent);
+void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc; +static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp) +{
return drmm_kmalloc(dev, size, gfp | __GFP_ZERO);
+}
+void drmm_kfree(struct drm_device *dev, void *data);
+#endif diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index ca7cee8e728a..1c9417430d08 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -313,6 +313,10 @@ enum drm_debug_category { * @DRM_UT_DP: Used in the DP code. */ DRM_UT_DP = 0x100,
/**
* @DRM_UT_DRMRES: Used in the drm managed resources code.
*/
DRM_UT_DRMRES = 0x200,
};
static inline bool drm_debug_enabled(enum drm_debug_category category) @@ -442,6 +446,8 @@ void drm_dev_dbg(const struct device *dev, enum drm_debug_category category, drm_dev_dbg((drm)->dev, DRM_UT_LEASE, fmt, ##__VA_ARGS__) #define drm_dbg_dp(drm, fmt, ...) \ drm_dev_dbg((drm)->dev, DRM_UT_DP, fmt, ##__VA_ARGS__) +#define drm_dbg_drmres(drm, fmt, ...) \
drm_dev_dbg((drm)->dev, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
We use drmm for all the managed stuff. So drm_dbg_drmm() is for me a more logical name. This debug printing is used only in a few spots, so no big deal.
devres also has this funny split of devres internally, but devm_ for the functions drivers should use. So yeah this is entirely for drm_managed.c only. -Daniel -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
Hi Daniel.
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9fcd6ab3c154..3e5627d6eba6 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -629,6 +629,9 @@ int drm_dev_init(struct drm_device *dev, dev->dev = get_device(parent); dev->driver = driver;
INIT_LIST_HEAD(&dev->managed.resources);
spin_lock_init(&dev->managed.lock);
/* no per-device feature limits by default */ dev->driver_features = ~0u;
@@ -828,8 +831,16 @@ static void drm_dev_release(struct kref *ref) dev->driver->release(dev); } else { drm_dev_fini(dev);
kfree(dev);
if (!dev->managed.final_kfree) {
WARN_ON(!list_empty(&dev->managed.resources));
kfree(dev);
}
This looks sub-optimal. We cannot be sure a driver have used drmm_add_final_kfree() if it makes use of drmm_. So we may not WARN in all relavant cases. Also, we cannot expect all drivers that uses devmm_ to have managed to get rid of their ->release call-back.
The above is purely transition code. It gets cleaned up once all drivers call drmm_add_final_kfree(). This all disappears again, but indeed looks like the interim state isn't quite what we want.
So the right thing looks to me like we should move it out to be unconditional. Se we will WARN_ON(!list_empty(&dev->managed.resources)) always.
Until the driver has set drmm_add_final_kfree it's actually dangerous to use the drmm stuff. Exactly because of the use-after-free you point out below. Hence the warning to make sure there's no release actions. I'll shuffle this around to make sure we call kfree last for all possible paths and make sure this bisects all correctly.
I was just reviewing the code I had on hand, and did not look further in the set of patches. Very good if we can keep is bisectable.
- Based on drivers/base/devres.c
- */
+#include <drm/drm_managed.h>
+#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h>
+#include <drm/drm_device.h> +#include <drm/drm_print.h>
It is good practice to group the include files. And drm/ comes after linux/
I try to put the main header first to make sure it's stand-alone, but I guess that works with the header check now? Do I need to do anything to get that checked?
The header-check infrastructure was dropped again - see: fcbb8461fd2376ba3782b5b8bd440c929b8e4980
So including it as the first header in the implmentation file is likely the best way to keep it self contained. We will spot errors sooner.
+static __always_inline struct drmres * alloc_dr(drmres_release_t release,
size_t size, gfp_t gfp, int nid)
Why do we force the compiler to inline this? Seems a little agressive.
It's not for performance, but for kmalloc_trace_caller. No point if our caller is always some boring function from drm_managed.c that calls alloc_dr. If we force alloc_dr to inline, then we get the caller of the drm_managed.c function traced as allocator. Much better.
(I stole that trick from devres.c)
I'll add a comment to explain this.
Thanks.
All the two users so far uses dev_to_node(dev->dev) for the nid. Maybe let this function take a drm_device * and thus move the calculation to this function?
Copypastes like that :-) I feel somewhat meh here ...
Well - keep the diff for devres smaller for now and leave it. It was just an observation.
/**
* @managed:
*
* Managed resources linked to the lifetime of this &drm_device as
* tracked by @ref.
*/
struct {
struct list_head resources;
void *final_kfree;
spinlock_t lock;
} managed;
I am missing kernel-doc here. At least document that lock is used to guard access to resources. (s/lock/lock_resources/ ?)
Dunno why, but the support for name sub-structures seems to have broken in kerneldoc. So I can type it, but it's not showing up, so I didn't bother. Well I had it, but deleted it again. It's still documented to work, but I have no idea what I'm doing wrong.
Most readers prefer the .c files as the source. I personally read the generated kernel doc when I google and when I check that my own stuff looks good in kernel-doc format. So comments are still valueable despite not being picked up by kernel-doc. You know this - but I just wanted to encourage you to write the few lines that may help me and others :-)
Sam
On Sat, Feb 29, 2020 at 12:17 PM Sam Ravnborg sam@ravnborg.org wrote:
- Based on drivers/base/devres.c
- */
+#include <drm/drm_managed.h>
+#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h>
+#include <drm/drm_device.h> +#include <drm/drm_print.h>
It is good practice to group the include files. And drm/ comes after linux/
I try to put the main header first to make sure it's stand-alone, but I guess that works with the header check now? Do I need to do anything to get that checked?
The header-check infrastructure was dropped again - see: fcbb8461fd2376ba3782b5b8bd440c929b8e4980
Uh I'm disappoint :-/
Adding Jani in case he missed this too. I guess maybe we should resurrect it for drm again (and with a file pattern starting in a .dot).
/**
* @managed:
*
* Managed resources linked to the lifetime of this &drm_device as
* tracked by @ref.
*/
struct {
struct list_head resources;
void *final_kfree;
spinlock_t lock;
} managed;
I am missing kernel-doc here. At least document that lock is used to guard access to resources. (s/lock/lock_resources/ ?)
Dunno why, but the support for name sub-structures seems to have broken in kerneldoc. So I can type it, but it's not showing up, so I didn't bother. Well I had it, but deleted it again. It's still documented to work, but I have no idea what I'm doing wrong.
Most readers prefer the .c files as the source. I personally read the generated kernel doc when I google and when I check that my own stuff looks good in kernel-doc format. So comments are still valueable despite not being picked up by kernel-doc. You know this - but I just wanted to encourage you to write the few lines that may help me and others :-)
Hm I thought way back this actually worked. Again ping for Jani, he's better on top of what's happening in kernel-doc land. -Daniel -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Sat, 29 Feb 2020, Daniel Vetter daniel.vetter@ffwll.ch wrote:
On Sat, Feb 29, 2020 at 12:17 PM Sam Ravnborg sam@ravnborg.org wrote:
The header-check infrastructure was dropped again - see: fcbb8461fd2376ba3782b5b8bd440c929b8e4980
Uh I'm disappoint :-/
To say the least. I thought it was a good *opt-in* feature for whoever wanted it. But the part that got the backlash was applying it to absolutely everything under include/. And then it got removed altogether. From one extreme to the other. Nuts.
Adding Jani in case he missed this too. I guess maybe we should resurrect it for drm again (and with a file pattern starting in a .dot).
We have a local implementation in i915/Makefile again. It uses 'find' to find the headers which is fine in i915, but the parameters need to be adjusted for drm to not be recursive. -maxdepth 1 or something. Also need to add another local config option. Sad trombone.
/**
* @managed:
*
* Managed resources linked to the lifetime of this &drm_device as
* tracked by @ref.
*/
struct {
struct list_head resources;
void *final_kfree;
spinlock_t lock;
} managed;
I am missing kernel-doc here. At least document that lock is used to guard access to resources. (s/lock/lock_resources/ ?)
Dunno why, but the support for name sub-structures seems to have broken in kerneldoc. So I can type it, but it's not showing up, so I didn't bother. Well I had it, but deleted it again. It's still documented to work, but I have no idea what I'm doing wrong.
Most readers prefer the .c files as the source. I personally read the generated kernel doc when I google and when I check that my own stuff looks good in kernel-doc format. So comments are still valueable despite not being picked up by kernel-doc. You know this - but I just wanted to encourage you to write the few lines that may help me and others :-)
Hm I thought way back this actually worked. Again ping for Jani, he's better on top of what's happening in kernel-doc land.
I haven't really been all that active lately, but I think the syntax here would be e.g. "@managed.resources:".
BR, Jani.
On Mon, Mar 02, 2020 at 11:22:34AM +0200, Jani Nikula wrote:
On Sat, 29 Feb 2020, Daniel Vetter daniel.vetter@ffwll.ch wrote:
On Sat, Feb 29, 2020 at 12:17 PM Sam Ravnborg sam@ravnborg.org wrote:
/**
* @managed:
*
* Managed resources linked to the lifetime of this &drm_device as
* tracked by @ref.
*/
struct {
struct list_head resources;
void *final_kfree;
spinlock_t lock;
} managed;
I am missing kernel-doc here. At least document that lock is used to guard access to resources. (s/lock/lock_resources/ ?)
Dunno why, but the support for name sub-structures seems to have broken in kerneldoc. So I can type it, but it's not showing up, so I didn't bother. Well I had it, but deleted it again. It's still documented to work, but I have no idea what I'm doing wrong.
Most readers prefer the .c files as the source. I personally read the generated kernel doc when I google and when I check that my own stuff looks good in kernel-doc format. So comments are still valueable despite not being picked up by kernel-doc. You know this - but I just wanted to encourage you to write the few lines that may help me and others :-)
Hm I thought way back this actually worked. Again ping for Jani, he's better on top of what's happening in kernel-doc land.
I haven't really been all that active lately, but I think the syntax here would be e.g. "@managed.resources:".
That's the one that doesn't seem to work unfortunately.
Adding kerneldoc people and mailing list, maybe this was intentionally removed somewhen ... Jon, any pointers? -Daniel
On Mon, Mar 02, 2020 at 11:22:34AM +0200, Jani Nikula wrote:
On Sat, 29 Feb 2020, Daniel Vetter daniel.vetter@ffwll.ch wrote:
On Sat, Feb 29, 2020 at 12:17 PM Sam Ravnborg sam@ravnborg.org wrote:
The header-check infrastructure was dropped again - see: fcbb8461fd2376ba3782b5b8bd440c929b8e4980
Uh I'm disappoint :-/
To say the least. I thought it was a good *opt-in* feature for whoever wanted it. But the part that got the backlash was applying it to absolutely everything under include/. And then it got removed altogether. From one extreme to the other. Nuts.
Adding Jani in case he missed this too. I guess maybe we should resurrect it for drm again (and with a file pattern starting in a .dot).
We have a local implementation in i915/Makefile again. It uses 'find' to find the headers which is fine in i915, but the parameters need to be adjusted for drm to not be recursive. -maxdepth 1 or something. Also need to add another local config option. Sad trombone.
Splitting this up into two threads.
Could we extend this to drm headers again too? Sad thrombones indeed, but at least here we could still the proper fanfares ... Maybe something like have the Makefile snippet in drivers/gpu and then keep a list of directories (or file glob patterns probably better) in there that it should check.
I really liked this entire idea very much.
Oh also maybe switch the temp files over to dotfiles, so Linus doesn't get upset (which really I think is all that Linus expected, but I guess people just panic and revert). -Daniel
Hi Daniel / Jani.
On Mon, Mar 02, 2020 at 11:22:34AM +0200, Jani Nikula wrote:
On Sat, 29 Feb 2020, Daniel Vetter daniel.vetter@ffwll.ch wrote:
On Sat, Feb 29, 2020 at 12:17 PM Sam Ravnborg sam@ravnborg.org wrote:
The header-check infrastructure was dropped again - see: fcbb8461fd2376ba3782b5b8bd440c929b8e4980
Uh I'm disappoint :-/
To say the least. I thought it was a good *opt-in* feature for whoever wanted it. But the part that got the backlash was applying it to absolutely everything under include/. And then it got removed altogether. From one extreme to the other. Nuts.
Adding Jani in case he missed this too. I guess maybe we should resurrect it for drm again (and with a file pattern starting in a .dot).
We have a local implementation in i915/Makefile again. It uses 'find' to find the headers which is fine in i915, but the parameters need to be adjusted for drm to not be recursive. -maxdepth 1 or something. Also need to add another local config option. Sad trombone.
Splitting this up into two threads.
Could we extend this to drm headers again too? Sad thrombones indeed, but at least here we could still the proper fanfares ... Maybe something like have the Makefile snippet in drivers/gpu and then keep a list of directories (or file glob patterns probably better) in there that it should check.
I really liked this entire idea very much.
Oh also maybe switch the temp files over to dotfiles, so Linus doesn't get upset (which really I think is all that Linus expected, but I guess people just panic and revert).
I will try to give it a spin by adding the feature back to kbuild, but without the excessive use. And with dot-files so the run does not disturb. So we avoid different sub-systemes makes there own small solutions.
Give me a few weeks - need to land some exciting (not) binding patches for panel/ first. Anyone else up to the task, then I will be happy to review.
Sam
I also did a full review of all callers, and only the xen driver forgot to call drm_dev_put in the failure path. Fix that up too.
v2: I noticed that xen has a drm_driver.release hook, and uses drm_dev_alloc(). We need to remove the kfree from xen_drm_drv_release().
bochs also has a release hook, but leaked the drm_device ever since
commit 0a6659bdc5e8221da99eebb176fd9591435e38de Author: Gerd Hoffmann kraxel@redhat.com Date: Tue Dec 17 18:04:46 2013 +0100
drm/bochs: new driver
This patch here fixes that leak.
Same for virtio, started leaking with
commit b1df3a2b24a917f8853d43fe9683c0e360d2c33a Author: Gerd Hoffmann kraxel@redhat.com Date: Tue Feb 11 14:58:04 2020 +0100
drm/virtio: add drm_driver.release callback.
Cc: Gerd Hoffmann kraxel@redhat.com Cc: Oleksandr Andrushchenko oleksandr_andrushchenko@epam.com Cc: xen-devel@lists.xenproject.org
Reviewed-by: Oleksandr Andrushchenko oleksandr_andrushchenko@epam.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maxime Ripard mripard@kernel.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Oleksandr Andrushchenko oleksandr_andrushchenko@epam.com Cc: xen-devel@lists.xenproject.org --- drivers/gpu/drm/drm_drv.c | 3 +++ drivers/gpu/drm/xen/xen_drm_front.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3e5627d6eba6..9e62e28bbc62 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -39,6 +39,7 @@ #include <drm/drm_color_mgmt.h> #include <drm/drm_drv.h> #include <drm/drm_file.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_object.h> #include <drm/drm_print.h>
@@ -819,6 +820,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, return ERR_PTR(ret); }
+ drmm_add_final_kfree(dev, dev); + return dev; } EXPORT_SYMBOL(drm_dev_alloc); diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c index 4be49c1aef51..d22b5da38935 100644 --- a/drivers/gpu/drm/xen/xen_drm_front.c +++ b/drivers/gpu/drm/xen/xen_drm_front.c @@ -461,7 +461,6 @@ static void xen_drm_drv_release(struct drm_device *dev) drm_mode_config_cleanup(dev);
drm_dev_fini(dev); - kfree(dev);
if (front_info->cfg.be_alloc) xenbus_switch_state(front_info->xb_dev, @@ -561,6 +560,7 @@ static int xen_drm_drv_init(struct xen_drm_front_info *front_info) fail_modeset: drm_kms_helper_poll_fini(drm_dev); drm_mode_config_cleanup(drm_dev); + drm_dev_put(drm_dev); fail: kfree(drm_info); return ret;
They all share mipi_dbi_release so we need to switch them all together. With this we can drop the final kfree from the release function.
Aside, I think we could perhaps have a tiny additional helper for these mipi_dbi drivers, the first few lines around devm_drm_dev_init are all the same (except for the drm_driver pointer).
Reviewed-by: Noralf Trønnes noralf@tronnes.org Tested-by: Noralf Trønnes noralf@tronnes.org Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maxime Ripard mripard@kernel.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Eric Anholt eric@anholt.net Cc: David Lechner david@lechnology.com Cc: Kamlesh Gurudasani kamlesh.gurudasani@gmail.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_mipi_dbi.c | 3 --- drivers/gpu/drm/tiny/hx8357d.c | 2 ++ drivers/gpu/drm/tiny/ili9225.c | 2 ++ drivers/gpu/drm/tiny/ili9341.c | 2 ++ drivers/gpu/drm/tiny/ili9486.c | 2 ++ drivers/gpu/drm/tiny/mi0283qt.c | 2 ++ drivers/gpu/drm/tiny/st7586.c | 2 ++ drivers/gpu/drm/tiny/st7735r.c | 2 ++ 8 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 558baf989f5a..069603dfcd10 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -588,13 +588,10 @@ EXPORT_SYMBOL(mipi_dbi_dev_init); */ void mipi_dbi_release(struct drm_device *drm) { - struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(drm); - DRM_DEBUG_DRIVER("\n");
drm_mode_config_cleanup(drm); drm_dev_fini(drm); - kfree(dbidev); } EXPORT_SYMBOL(mipi_dbi_release);
diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index 9af8ff84974f..42bc5dadcb1c 100644 --- a/drivers/gpu/drm/tiny/hx8357d.c +++ b/drivers/gpu/drm/tiny/hx8357d.c @@ -21,6 +21,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h> #include <video/mipi_display.h> @@ -236,6 +237,7 @@ static int hx8357d_probe(struct spi_device *spi) kfree(dbidev); return ret; } + drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index 802fb8dde1b6..aae88dc5b3f7 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -24,6 +24,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_rect.h>
@@ -387,6 +388,7 @@ static int ili9225_probe(struct spi_device *spi) kfree(dbidev); return ret; } + drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 33b51dc7faa8..7d40cb4ff72b 100644 --- a/drivers/gpu/drm/tiny/ili9341.c +++ b/drivers/gpu/drm/tiny/ili9341.c @@ -20,6 +20,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h> #include <video/mipi_display.h> @@ -194,6 +195,7 @@ static int ili9341_probe(struct spi_device *spi) kfree(dbidev); return ret; } + drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 5084b38c1a71..7d735fc67498 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -19,6 +19,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h>
@@ -208,6 +209,7 @@ static int ili9486_probe(struct spi_device *spi) kfree(dbidev); return ret; } + drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c index e2cfd9a17143..8555a56bce8c 100644 --- a/drivers/gpu/drm/tiny/mi0283qt.c +++ b/drivers/gpu/drm/tiny/mi0283qt.c @@ -18,6 +18,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h> #include <video/mipi_display.h> @@ -198,6 +199,7 @@ static int mi0283qt_probe(struct spi_device *spi) kfree(dbidev); return ret; } + drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 9ef559dd3191..427c2561f5f4 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -21,6 +21,7 @@ #include <drm/drm_format_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_rect.h>
@@ -328,6 +329,7 @@ static int st7586_probe(struct spi_device *spi) kfree(dbidev); return ret; } + drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index 18b925df6e51..b447235c3d47 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -21,6 +21,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h>
#define ST7735R_FRMCTR1 0xb1 @@ -209,6 +210,7 @@ static int st7735r_probe(struct spi_device *spi) kfree(dbidev); return ret; } + drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
On Thu, Feb 27, 2020 at 07:14:36PM +0100, Daniel Vetter wrote:
They all share mipi_dbi_release so we need to switch them all together. With this we can drop the final kfree from the release function.
Aside, I think we could perhaps have a tiny additional helper for these mipi_dbi drivers, the first few lines around devm_drm_dev_init are all the same (except for the drm_driver pointer).
Reviewed-by: Noralf Trønnes noralf@tronnes.org Tested-by: Noralf Trønnes noralf@tronnes.org Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maxime Ripard mripard@kernel.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Eric Anholt eric@anholt.net Cc: David Lechner david@lechnology.com Cc: Kamlesh Gurudasani kamlesh.gurudasani@gmail.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
Acked-by: Sam Ravnborg sam@ravnborg.org
drivers/gpu/drm/drm_mipi_dbi.c | 3 --- drivers/gpu/drm/tiny/hx8357d.c | 2 ++ drivers/gpu/drm/tiny/ili9225.c | 2 ++ drivers/gpu/drm/tiny/ili9341.c | 2 ++ drivers/gpu/drm/tiny/ili9486.c | 2 ++ drivers/gpu/drm/tiny/mi0283qt.c | 2 ++ drivers/gpu/drm/tiny/st7586.c | 2 ++ drivers/gpu/drm/tiny/st7735r.c | 2 ++ 8 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 558baf989f5a..069603dfcd10 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -588,13 +588,10 @@ EXPORT_SYMBOL(mipi_dbi_dev_init); */ void mipi_dbi_release(struct drm_device *drm) {
struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(drm);
DRM_DEBUG_DRIVER("\n");
drm_mode_config_cleanup(drm); drm_dev_fini(drm);
kfree(dbidev);
} EXPORT_SYMBOL(mipi_dbi_release);
diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index 9af8ff84974f..42bc5dadcb1c 100644 --- a/drivers/gpu/drm/tiny/hx8357d.c +++ b/drivers/gpu/drm/tiny/hx8357d.c @@ -21,6 +21,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h> #include <video/mipi_display.h> @@ -236,6 +237,7 @@ static int hx8357d_probe(struct spi_device *spi) kfree(dbidev); return ret; }
drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index 802fb8dde1b6..aae88dc5b3f7 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -24,6 +24,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_rect.h>
@@ -387,6 +388,7 @@ static int ili9225_probe(struct spi_device *spi) kfree(dbidev); return ret; }
drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 33b51dc7faa8..7d40cb4ff72b 100644 --- a/drivers/gpu/drm/tiny/ili9341.c +++ b/drivers/gpu/drm/tiny/ili9341.c @@ -20,6 +20,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h> #include <video/mipi_display.h> @@ -194,6 +195,7 @@ static int ili9341_probe(struct spi_device *spi) kfree(dbidev); return ret; }
drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 5084b38c1a71..7d735fc67498 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -19,6 +19,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h>
@@ -208,6 +209,7 @@ static int ili9486_probe(struct spi_device *spi) kfree(dbidev); return ret; }
drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c index e2cfd9a17143..8555a56bce8c 100644 --- a/drivers/gpu/drm/tiny/mi0283qt.c +++ b/drivers/gpu/drm/tiny/mi0283qt.c @@ -18,6 +18,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modeset_helper.h> #include <video/mipi_display.h> @@ -198,6 +199,7 @@ static int mi0283qt_probe(struct spi_device *spi) kfree(dbidev); return ret; }
drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 9ef559dd3191..427c2561f5f4 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -21,6 +21,7 @@ #include <drm/drm_format_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_rect.h>
@@ -328,6 +329,7 @@ static int st7586_probe(struct spi_device *spi) kfree(dbidev); return ret; }
drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index 18b925df6e51..b447235c3d47 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -21,6 +21,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h>
#define ST7735R_FRMCTR1 0xb1 @@ -209,6 +210,7 @@ static int st7735r_probe(struct spi_device *spi) kfree(dbidev); return ret; }
drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
-- 2.24.1
With this we can drop the final kfree from the release function.
v2: We need drm_dev_put to unroll the driver creation (once drm_dev_init and drmm_add_final_kfree suceeded), otherwise the drmm_ magic doesn't happen.
v3: Actually squash in the fixup (Laurent).
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Sam Ravnborg sam@ravnborg.org --- drivers/gpu/drm/udl/udl_drv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index e6c1cd77d4d4..6a5594946096 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -10,6 +10,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_ioctl.h> #include <drm/drm_probe_helper.h> #include <drm/drm_print.h> @@ -38,7 +39,6 @@ static void udl_driver_release(struct drm_device *dev) udl_fini(dev); udl_modeset_cleanup(dev); drm_dev_fini(dev); - kfree(dev); }
static struct drm_driver driver = { @@ -77,11 +77,11 @@ static struct udl_device *udl_driver_create(struct usb_interface *interface)
udl->udev = udev; udl->drm.dev_private = udl; + drmm_add_final_kfree(&udl->drm, udl);
r = udl_init(udl); if (r) { - drm_dev_fini(&udl->drm); - kfree(udl); + drm_dev_put(&udl->drm); return ERR_PTR(r); }
On Thu, Feb 27, 2020 at 07:14:37PM +0100, Daniel Vetter wrote:
With this we can drop the final kfree from the release function.
v2: We need drm_dev_put to unroll the driver creation (once drm_dev_init and drmm_add_final_kfree suceeded), otherwise the drmm_ magic doesn't happen.
v3: Actually squash in the fixup (Laurent).
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Sam Ravnborg sam@ravnborg.org
Acked-by: Sam Ravnborg sam@ravnborg.org
drivers/gpu/drm/udl/udl_drv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index e6c1cd77d4d4..6a5594946096 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -10,6 +10,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_ioctl.h> #include <drm/drm_probe_helper.h> #include <drm/drm_print.h> @@ -38,7 +39,6 @@ static void udl_driver_release(struct drm_device *dev) udl_fini(dev); udl_modeset_cleanup(dev); drm_dev_fini(dev);
- kfree(dev);
}
static struct drm_driver driver = { @@ -77,11 +77,11 @@ static struct udl_device *udl_driver_create(struct usb_interface *interface)
udl->udev = udev; udl->drm.dev_private = udl;
drmm_add_final_kfree(&udl->drm, udl);
r = udl_init(udl); if (r) {
drm_dev_fini(&udl->drm);
kfree(udl);
return ERR_PTR(r); }drm_dev_put(&udl->drm);
-- 2.24.1
With this we can drop the final kfree from the release function.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: virtualization@lists.linux-foundation.org Cc: spice-devel@lists.freedesktop.org --- drivers/gpu/drm/qxl/qxl_drv.c | 2 -- drivers/gpu/drm/qxl/qxl_kms.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 4fda3f9b29f4..09102e2efabc 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -144,8 +144,6 @@ static void qxl_drm_release(struct drm_device *dev) */ qxl_modeset_fini(qdev); qxl_device_fini(qdev); - dev->dev_private = NULL; - kfree(qdev); }
static void diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index 70b20ee4741a..09d7b5f6d172 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -27,6 +27,7 @@ #include <linux/pci.h>
#include <drm/drm_drv.h> +#include <drm/drm_managed.h> #include <drm/drm_probe_helper.h>
#include "qxl_drv.h" @@ -121,6 +122,7 @@ int qxl_device_init(struct qxl_device *qdev, qdev->ddev.pdev = pdev; pci_set_drvdata(pdev, &qdev->ddev); qdev->ddev.dev_private = qdev; + drmm_add_final_kfree(&qdev->ddev, qdev);
mutex_init(&qdev->gem.mutex); mutex_init(&qdev->update_area_mutex);
With this we can drop the final kfree from the release function.
The mock device in the selftests needed it's pci_device split up from the drm_device. In the future we could simplify this again by allocating the pci_device as a managed allocation too.
v2: I overlooked that i915_driver_destroy is also called in the unwind code of the error path. There we need a drm_dev_put. Similar for the mock object.
Now the problem with that is that the drm_driver->release callbacks for both the real driver and the mock one assume everything has been set up. Hence going through that path for a partially set up driver will result in issues. Quickest fix is to disable the ->release() hook until the driver is fully initialized, and keep the onion unwinding. Long term would be cleanest to move everything over to drmm_ release actions, but that's a lot of work for a big driver like i915. Plus more core work needed first anyway.
v3: Fix i915_drm pointer wrangling in mock_gem_device. Also switch over to start using drm_dev_put() to clean up even on the error path. Aside I think the current error path is leaking the allocation.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Jani Nikula jani.nikula@linux.intel.com Cc: Joonas Lahtinen joonas.lahtinen@linux.intel.com Cc: Rodrigo Vivi rodrigo.vivi@intel.com Cc: Chris Wilson chris@chris-wilson.co.uk Cc: Tvrtko Ursulin tvrtko.ursulin@intel.com Cc: Matthew Auld matthew.auld@intel.com Cc: Andi Shyti andi.shyti@intel.com Cc: Mika Kuoppala mika.kuoppala@linux.intel.com Cc: Daniele Ceraolo Spurio daniele.ceraolospurio@intel.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Abdiel Janulgue abdiel.janulgue@linux.intel.com Cc: intel-gfx@lists.freedesktop.org --- drivers/gpu/drm/i915/i915_drv.c | 10 +++++- drivers/gpu/drm/i915/i915_drv.h | 3 ++ .../gpu/drm/i915/selftests/mock_gem_device.c | 31 ++++++++++++++----- 3 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 0b59a9bd2581..4119e57b0c5b 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -43,6 +43,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_ioctl.h> #include <drm/drm_irq.h> +#include <drm/drm_managed.h> #include <drm/drm_probe_helper.h>
#include "display/intel_acpi.h" @@ -894,6 +895,8 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) return ERR_PTR(err); }
+ drmm_add_final_kfree(&i915->drm, i915); + i915->drm.pdev = pdev; pci_set_drvdata(pdev, i915);
@@ -912,7 +915,6 @@ static void i915_driver_destroy(struct drm_i915_private *i915) struct pci_dev *pdev = i915->drm.pdev;
drm_dev_fini(&i915->drm); - kfree(i915); }
/** @@ -996,6 +998,8 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
i915_welcome_messages(i915);
+ i915->do_release = true; + return 0;
out_cleanup_irq: @@ -1016,6 +1020,7 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) out_fini: i915_probe_error(i915, "Device initialization failed (%d)\n", ret); i915_driver_destroy(i915); + drm_dev_put(&i915->drm); return ret; }
@@ -1055,6 +1060,9 @@ static void i915_driver_release(struct drm_device *dev) struct drm_i915_private *dev_priv = to_i915(dev); struct intel_runtime_pm *rpm = &dev_priv->runtime_pm;
+ if (!dev_priv->do_release) + return; + disable_rpm_wakeref_asserts(rpm);
i915_gem_driver_release(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ea13fc0b409b..31c40e35f497 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -884,6 +884,9 @@ struct i915_selftest_stash { struct drm_i915_private { struct drm_device drm;
+ /* FIXME: Device release actions should all be moved to drmm_ */ + bool do_release; + const struct intel_device_info __info; /* Use INTEL_INFO() to access. */ struct intel_runtime_info __runtime; /* Use RUNTIME_INFO() to access. */ struct intel_driver_caps caps; diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c index 754d0eb6beaa..c85bbc88f504 100644 --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c @@ -25,6 +25,8 @@ #include <linux/pm_domain.h> #include <linux/pm_runtime.h>
+#include <drm/drm_managed.h> + #include "gt/intel_gt.h" #include "gt/intel_gt_requests.h" #include "gt/mock_engine.h" @@ -55,6 +57,9 @@ static void mock_device_release(struct drm_device *dev) { struct drm_i915_private *i915 = to_i915(dev);
+ if (!i915->do_release) + goto out; + mock_device_flush(i915); intel_gt_driver_remove(&i915->gt);
@@ -72,7 +77,9 @@ static void mock_device_release(struct drm_device *dev) drm_mode_config_cleanup(&i915->drm);
drm_dev_fini(&i915->drm); +out: put_device(&i915->drm.pdev->dev); + kfree(i915->drm.pdev); }
static struct drm_driver mock_driver = { @@ -114,9 +121,14 @@ struct drm_i915_private *mock_gem_device(void) struct pci_dev *pdev; int err;
- pdev = kzalloc(sizeof(*pdev) + sizeof(*i915), GFP_KERNEL); + pdev = kzalloc(sizeof(*pdev), GFP_KERNEL); if (!pdev) - goto err; + return NULL; + i915 = kzalloc(sizeof(*i915), GFP_KERNEL); + if (!i915) { + kfree(pdev); + return NULL; + }
device_initialize(&pdev->dev); pdev->class = PCI_BASE_CLASS_DISPLAY << 16; @@ -129,7 +141,6 @@ struct drm_i915_private *mock_gem_device(void) pdev->dev.archdata.iommu = (void *)-1; #endif
- i915 = (struct drm_i915_private *)(pdev + 1); pci_set_drvdata(pdev, i915);
dev_pm_domain_set(&pdev->dev, &pm_domain); @@ -141,9 +152,14 @@ struct drm_i915_private *mock_gem_device(void) err = drm_dev_init(&i915->drm, &mock_driver, &pdev->dev); if (err) { pr_err("Failed to initialise mock GEM device: err=%d\n", err); - goto put_device; + put_device(&pdev->dev); + kfree(pdev); + kfree(i915); + + return NULL; } i915->drm.pdev = pdev; + drmm_add_final_kfree(&i915->drm, i915);
intel_runtime_pm_init_early(&i915->runtime_pm);
@@ -188,6 +204,8 @@ struct drm_i915_private *mock_gem_device(void) __clear_bit(I915_WEDGED, &i915->gt.reset.flags); intel_engines_driver_register(i915);
+ i915->do_release = true; + return i915;
err_context: @@ -199,8 +217,7 @@ struct drm_i915_private *mock_gem_device(void) intel_memory_regions_driver_release(i915); drm_mode_config_cleanup(&i915->drm); drm_dev_fini(&i915->drm); -put_device: - put_device(&pdev->dev); -err: + drm_dev_put(&i915->drm); + return NULL; }
With this we can drop the final kfree from the release function.
I also noticed that cirrus forgot to call drm_dev_fini().
v2: Don't call kfree(cirrus) after we've handed overship of that to drm_device and the drmm_ stuff.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Linus Walleij linus.walleij@linaro.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: virtualization@lists.linux-foundation.org --- drivers/gpu/drm/cirrus/cirrus.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index d2ff63ce8eaf..2232556ce34c 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c @@ -35,6 +35,7 @@ #include <drm/drm_gem_shmem_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> @@ -527,10 +528,8 @@ static void cirrus_mode_config_init(struct cirrus_device *cirrus)
static void cirrus_release(struct drm_device *dev) { - struct cirrus_device *cirrus = dev->dev_private; - drm_mode_config_cleanup(dev); - kfree(cirrus); + drm_dev_fini(dev); }
DEFINE_DRM_GEM_FOPS(cirrus_fops); @@ -575,9 +574,12 @@ static int cirrus_pci_probe(struct pci_dev *pdev,
dev = &cirrus->dev; ret = drm_dev_init(dev, &cirrus_driver, &pdev->dev); - if (ret) - goto err_free_cirrus; + if (ret) { + kfree(cirrus); + goto err_pci_release; + } dev->dev_private = cirrus; + drmm_add_final_kfree(dev, cirrus);
ret = -ENOMEM; cirrus->vram = ioremap(pci_resource_start(pdev, 0), @@ -618,8 +620,6 @@ static int cirrus_pci_probe(struct pci_dev *pdev, iounmap(cirrus->vram); err_dev_put: drm_dev_put(dev); -err_free_cirrus: - kfree(cirrus); err_pci_release: pci_release_regions(pdev); return ret;
On Thu, Feb 27, 2020 at 07:14:40PM +0100, Daniel Vetter wrote:
With this we can drop the final kfree from the release function.
I also noticed that cirrus forgot to call drm_dev_fini().
v2: Don't call kfree(cirrus) after we've handed overship of that to drm_device and the drmm_ stuff.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Linus Walleij linus.walleij@linaro.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: virtualization@lists.linux-foundation.org
Acked-by: Sam Ravnborg sam@ravnborg.org
drivers/gpu/drm/cirrus/cirrus.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index d2ff63ce8eaf..2232556ce34c 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c @@ -35,6 +35,7 @@ #include <drm/drm_gem_shmem_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> @@ -527,10 +528,8 @@ static void cirrus_mode_config_init(struct cirrus_device *cirrus)
static void cirrus_release(struct drm_device *dev) {
- struct cirrus_device *cirrus = dev->dev_private;
- drm_mode_config_cleanup(dev);
- kfree(cirrus);
- drm_dev_fini(dev);
}
DEFINE_DRM_GEM_FOPS(cirrus_fops); @@ -575,9 +574,12 @@ static int cirrus_pci_probe(struct pci_dev *pdev,
dev = &cirrus->dev; ret = drm_dev_init(dev, &cirrus_driver, &pdev->dev);
- if (ret)
goto err_free_cirrus;
if (ret) {
kfree(cirrus);
goto err_pci_release;
} dev->dev_private = cirrus;
drmm_add_final_kfree(dev, cirrus);
ret = -ENOMEM; cirrus->vram = ioremap(pci_resource_start(pdev, 0),
@@ -618,8 +620,6 @@ static int cirrus_pci_probe(struct pci_dev *pdev, iounmap(cirrus->vram); err_dev_put: drm_dev_put(dev); -err_free_cirrus:
- kfree(cirrus);
err_pci_release: pci_release_regions(pdev); return ret; -- 2.24.1
With this we can drop the final kfree from the release function.
I also noticed that the unwind code is wrong, after drm_dev_init the drm_device owns the v3d allocation, so the kfree(v3d) is a double-free. Reorder the setup to fix this issue.
After a bit more prep in drivers and drm core v3d should be able to switch over to devm_drm_dev_init, which should clean this up further.
Acked-by: Eric Anholt eric@anholt.net Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Eric Anholt eric@anholt.net --- drivers/gpu/drm/v3d/v3d_drv.c | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index eaa8e9682373..8d0c0daaac81 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -25,6 +25,7 @@ #include <drm/drm_drv.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_managed.h> #include <uapi/drm/v3d_drm.h>
#include "v3d_drv.h" @@ -257,13 +258,23 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) v3d->pdev = pdev; drm = &v3d->drm;
+ ret = drm_dev_init(&v3d->drm, &v3d_drm_driver, dev); + if (ret) { + kfree(v3d); + return ret; + } + + platform_set_drvdata(pdev, drm); + drm->dev_private = v3d; + drmm_add_final_kfree(drm, v3d); + ret = map_regs(v3d, &v3d->hub_regs, "hub"); if (ret) - goto dev_free; + goto dev_destroy;
ret = map_regs(v3d, &v3d->core_regs[0], "core0"); if (ret) - goto dev_free; + goto dev_destroy;
mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO); dev->coherent_dma_mask = @@ -281,21 +292,21 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) ret = PTR_ERR(v3d->reset);
if (ret == -EPROBE_DEFER) - goto dev_free; + goto dev_destroy;
v3d->reset = NULL; ret = map_regs(v3d, &v3d->bridge_regs, "bridge"); if (ret) { dev_err(dev, "Failed to get reset control or bridge regs\n"); - goto dev_free; + goto dev_destroy; } }
if (v3d->ver < 41) { ret = map_regs(v3d, &v3d->gca_regs, "gca"); if (ret) - goto dev_free; + goto dev_destroy; }
v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr, @@ -303,23 +314,16 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) if (!v3d->mmu_scratch) { dev_err(dev, "Failed to allocate MMU scratch page\n"); ret = -ENOMEM; - goto dev_free; + goto dev_destroy; }
pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 50); pm_runtime_enable(dev);
- ret = drm_dev_init(&v3d->drm, &v3d_drm_driver, dev); - if (ret) - goto dma_free; - - platform_set_drvdata(pdev, drm); - drm->dev_private = v3d; - ret = v3d_gem_init(drm); if (ret) - goto dev_destroy; + goto dma_free;
ret = v3d_irq_init(v3d); if (ret) @@ -335,12 +339,10 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) v3d_irq_disable(v3d); gem_destroy: v3d_gem_destroy(drm); -dev_destroy: - drm_dev_put(drm); dma_free: dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr); -dev_free: - kfree(v3d); +dev_destroy: + drm_dev_put(drm); return ret; }
With this we can drop the final kfree from the release function.
Acked-by: Jyri Sarha jsarha@ti.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Jyri Sarha jsarha@ti.com Cc: Tomi Valkeinen tomi.valkeinen@ti.com --- drivers/gpu/drm/tidss/tidss_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index d95e4be2c7b9..32a85628dbec 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -17,6 +17,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_managed.h> #include <drm/drm_probe_helper.h>
#include "tidss_dispc.h" @@ -109,8 +110,6 @@ static void tidss_release(struct drm_device *ddev) tidss_modeset_cleanup(tidss);
drm_dev_fini(ddev); - - kfree(tidss); }
DEFINE_DRM_GEM_CMA_FOPS(tidss_fops); @@ -154,6 +153,7 @@ static int tidss_probe(struct platform_device *pdev) kfree(ddev); return ret; } + drmm_add_final_kfree(ddev, tidss);
tidss->dev = dev; tidss->feat = of_device_get_match_data(dev);
With this we can drop the final kfree from the release function.
Reviewed-by: Linus Walleij linus.walleij@linaro.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Linus Walleij linus.walleij@linaro.org --- drivers/gpu/drm/mcde/mcde_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index f28cb7a576ba..7474481503a1 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -72,6 +72,7 @@ #include <drm/drm_gem.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_of.h> #include <drm/drm_probe_helper.h> #include <drm/drm_panel.h> @@ -223,7 +224,6 @@ static void mcde_release(struct drm_device *drm)
drm_mode_config_cleanup(drm); drm_dev_fini(drm); - kfree(mcde); }
DEFINE_DRM_GEM_CMA_FOPS(drm_fops); @@ -330,6 +330,7 @@ static int mcde_probe(struct platform_device *pdev) } drm = &mcde->drm; drm->dev_private = mcde; + drmm_add_final_kfree(drm, mcde); platform_set_drvdata(pdev, drm);
/* Enable continuous updates: this is what Linux' framebuffer expects */
With this we can drop the final kfree from the release function.
v2: After drm_dev_init/drmm_add_final_kfree we need to clean up everything through a drm_dev_put. Rework the unwind code to match that.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Emil Velikov emil.velikov@collabora.com Cc: Chris Wilson chris@chris-wilson.co.uk Cc: Sean Paul seanpaul@chromium.org Cc: Eric Anholt eric@anholt.net Cc: Rob Clark robdclark@chromium.org Cc: Sam Ravnborg sam@ravnborg.org --- drivers/gpu/drm/vgem/vgem_drv.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 909eba43664a..7486014e9149 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -39,6 +39,7 @@ #include <drm/drm_drv.h> #include <drm/drm_file.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_prime.h>
#include "vgem_drv.h" @@ -432,8 +433,6 @@ static void vgem_release(struct drm_device *dev)
platform_device_unregister(vgem->platform); drm_dev_fini(&vgem->drm); - - kfree(vgem); }
static struct drm_driver vgem_driver = { @@ -489,16 +488,19 @@ static int __init vgem_init(void) &vgem_device->platform->dev); if (ret) goto out_unregister; + drmm_add_final_kfree(&vgem_device->drm, vgem_device);
/* Final step: expose the device/driver to userspace */ - ret = drm_dev_register(&vgem_device->drm, 0); + ret = drm_dev_register(&vgem_device->drm, 0); if (ret) - goto out_fini; + goto out_put;
return 0;
-out_fini: - drm_dev_fini(&vgem_device->drm); +out_put: + drm_dev_put(&vgem_device->drm); + return ret; + out_unregister: platform_device_unregister(vgem_device->platform); out_free:
On Thu, Feb 27, 2020 at 07:14:44PM +0100, Daniel Vetter wrote:
With this we can drop the final kfree from the release function.
v2: After drm_dev_init/drmm_add_final_kfree we need to clean up everything through a drm_dev_put. Rework the unwind code to match that.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Emil Velikov emil.velikov@collabora.com Cc: Chris Wilson chris@chris-wilson.co.uk Cc: Sean Paul seanpaul@chromium.org Cc: Eric Anholt eric@anholt.net Cc: Rob Clark robdclark@chromium.org Cc: Sam Ravnborg sam@ravnborg.org
Acked-by: Sam Ravnborg sam@ravnborg.org
drivers/gpu/drm/vgem/vgem_drv.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 909eba43664a..7486014e9149 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -39,6 +39,7 @@ #include <drm/drm_drv.h> #include <drm/drm_file.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_prime.h>
#include "vgem_drv.h" @@ -432,8 +433,6 @@ static void vgem_release(struct drm_device *dev)
platform_device_unregister(vgem->platform); drm_dev_fini(&vgem->drm);
- kfree(vgem);
}
static struct drm_driver vgem_driver = { @@ -489,16 +488,19 @@ static int __init vgem_init(void) &vgem_device->platform->dev); if (ret) goto out_unregister;
drmm_add_final_kfree(&vgem_device->drm, vgem_device);
/* Final step: expose the device/driver to userspace */
- ret = drm_dev_register(&vgem_device->drm, 0);
- ret = drm_dev_register(&vgem_device->drm, 0); if (ret)
goto out_fini;
goto out_put;
return 0;
-out_fini:
- drm_dev_fini(&vgem_device->drm);
+out_put:
- drm_dev_put(&vgem_device->drm);
- return ret;
out_unregister: platform_device_unregister(vgem_device->platform); out_free: -- 2.24.1
With this we can drop the final kfree from the release function.
v2: After drm_dev_init/drmm_add_final_kfree we need to clean up everything through a drm_dev_put. Rework the unwind code to match that.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com Cc: Haneen Mohammed hamohammed.sa@gmail.com Cc: Daniel Vetter daniel@ffwll.ch --- drivers/gpu/drm/vkms/vkms_drv.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 860de052e820..2f35fe789343 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -21,6 +21,7 @@ #include <drm/drm_file.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h>
@@ -158,13 +159,14 @@ static int __init vkms_init(void) &vkms_device->platform->dev); if (ret) goto out_unregister; + drmm_add_final_kfree(&vkms_device->drm, vkms_device);
ret = dma_coerce_mask_and_coherent(vkms_device->drm.dev, DMA_BIT_MASK(64));
if (ret) { DRM_ERROR("Could not initialize DMA support\n"); - goto out_fini; + goto out_put; }
vkms_device->drm.irq_enabled = true; @@ -172,25 +174,25 @@ static int __init vkms_init(void) ret = drm_vblank_init(&vkms_device->drm, 1); if (ret) { DRM_ERROR("Failed to vblank\n"); - goto out_fini; + goto out_put; }
ret = vkms_modeset_init(vkms_device); if (ret) - goto out_fini; + goto out_put;
ret = drm_dev_register(&vkms_device->drm, 0); if (ret) - goto out_fini; + goto out_put;
return 0;
-out_fini: - drm_dev_fini(&vkms_device->drm); +out_put: + drm_dev_put(&vkms_device->drm); + return ret;
out_unregister: platform_device_unregister(vkms_device->platform); - out_free: kfree(vkms_device); return ret; @@ -205,8 +207,6 @@ static void __exit vkms_exit(void)
drm_dev_unregister(&vkms_device->drm); drm_dev_put(&vkms_device->drm); - - kfree(vkms_device); }
module_init(vkms_init);
Hi,
Thanks for the patch.
I reviewed and tested this patch, and everything looks fine.
Reviewed-by: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com Tested-by: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com
On 02/27, Daniel Vetter wrote:
With this we can drop the final kfree from the release function.
v2: After drm_dev_init/drmm_add_final_kfree we need to clean up everything through a drm_dev_put. Rework the unwind code to match that.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Rodrigo Siqueira rodrigosiqueiramelo@gmail.com Cc: Haneen Mohammed hamohammed.sa@gmail.com Cc: Daniel Vetter daniel@ffwll.ch
drivers/gpu/drm/vkms/vkms_drv.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 860de052e820..2f35fe789343 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -21,6 +21,7 @@ #include <drm/drm_file.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h>
@@ -158,13 +159,14 @@ static int __init vkms_init(void) &vkms_device->platform->dev); if (ret) goto out_unregister;
drmm_add_final_kfree(&vkms_device->drm, vkms_device);
ret = dma_coerce_mask_and_coherent(vkms_device->drm.dev, DMA_BIT_MASK(64));
if (ret) { DRM_ERROR("Could not initialize DMA support\n");
goto out_fini;
goto out_put;
}
vkms_device->drm.irq_enabled = true;
@@ -172,25 +174,25 @@ static int __init vkms_init(void) ret = drm_vblank_init(&vkms_device->drm, 1); if (ret) { DRM_ERROR("Failed to vblank\n");
goto out_fini;
goto out_put;
}
ret = vkms_modeset_init(vkms_device); if (ret)
goto out_fini;
goto out_put;
ret = drm_dev_register(&vkms_device->drm, 0); if (ret)
goto out_fini;
goto out_put;
return 0;
-out_fini:
- drm_dev_fini(&vkms_device->drm);
+out_put:
- drm_dev_put(&vkms_device->drm);
- return ret;
out_unregister: platform_device_unregister(vkms_device->platform);
out_free: kfree(vkms_device); return ret; @@ -205,8 +207,6 @@ static void __exit vkms_exit(void)
drm_dev_unregister(&vkms_device->drm); drm_dev_put(&vkms_device->drm);
- kfree(vkms_device);
}
module_init(vkms_init);
2.24.1
With this we can drop the final kfree from the release function.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: "Noralf Trønnes" noralf@tronnes.org --- drivers/gpu/drm/tiny/repaper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index f5ebcaf7ee3a..df5654ef53ee 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -31,6 +31,7 @@ #include <drm/drm_format_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_modes.h> #include <drm/drm_rect.h> #include <drm/drm_probe_helper.h> @@ -910,13 +911,10 @@ static const struct drm_mode_config_funcs repaper_mode_config_funcs = {
static void repaper_release(struct drm_device *drm) { - struct repaper_epd *epd = drm_to_epd(drm); - DRM_DEBUG_DRIVER("\n");
drm_mode_config_cleanup(drm); drm_dev_fini(drm); - kfree(epd); }
static const uint32_t repaper_formats[] = { @@ -1024,6 +1022,7 @@ static int repaper_probe(struct spi_device *spi) kfree(epd); return ret; } + drmm_add_final_kfree(drm, epd);
drm_mode_config_init(drm); drm->mode_config.funcs = &repaper_mode_config_funcs;
With this we can drop the final kfree from the release function.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Paul Cercueil paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index 9dfe7cb530e1..e2c832eb4e9a 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -23,6 +23,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_managed.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_plane.h> @@ -490,11 +491,8 @@ static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
static void ingenic_drm_release(struct drm_device *drm) { - struct ingenic_drm *priv = drm_device_get_priv(drm); - drm_mode_config_cleanup(drm); drm_dev_fini(drm); - kfree(priv); }
static int ingenic_drm_enable_vblank(struct drm_crtc *crtc) @@ -639,6 +637,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) kfree(priv); return ret; } + drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm); drm->mode_config.min_width = 0;
Hi Daniel,
Le jeu., févr. 27, 2020 at 19:14, Daniel Vetter daniel.vetter@ffwll.ch a écrit :
With this we can drop the final kfree from the release function.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Paul Cercueil paul@crapouillou.net
Reviewed-by: Paul Cercueil paul@crapouillou.net
Cheers, -Paul
drivers/gpu/drm/ingenic/ingenic-drm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index 9dfe7cb530e1..e2c832eb4e9a 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -23,6 +23,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_managed.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_plane.h> @@ -490,11 +491,8 @@ static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
static void ingenic_drm_release(struct drm_device *drm) {
- struct ingenic_drm *priv = drm_device_get_priv(drm);
- drm_mode_config_cleanup(drm); drm_dev_fini(drm);
- kfree(priv);
}
static int ingenic_drm_enable_vblank(struct drm_crtc *crtc) @@ -639,6 +637,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) kfree(priv); return ret; }
drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm); drm->mode_config.min_width = 0;
-- 2.24.1
With this we can drop the final kfree from the release function.
Reviewed-by: Hans de Goede hdegoede@redhat.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Hans de Goede hdegoede@redhat.com --- drivers/gpu/drm/tiny/gm12u320.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index a48173441ae0..524ca0941cf9 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -19,6 +19,7 @@ #include <drm/drm_gem_shmem_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> @@ -637,7 +638,6 @@ static void gm12u320_driver_release(struct drm_device *dev) gm12u320_usb_free(gm12u320); drm_mode_config_cleanup(dev); drm_dev_fini(dev); - kfree(gm12u320); }
DEFINE_DRM_GEM_FOPS(gm12u320_fops); @@ -692,6 +692,7 @@ static int gm12u320_usb_probe(struct usb_interface *interface, return ret; } dev->dev_private = gm12u320; + drmm_add_final_kfree(dev, gm12u320);
drm_mode_config_init(dev); dev->mode_config.min_width = GM12U320_USER_WIDTH;
These are the leftover drivers that didn't have a ->release hook that needed to be updated.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: "James (Qian) Wang" james.qian.wang@arm.com Cc: Liviu Dudau liviu.dudau@arm.com Cc: Mihail Atanassov mihail.atanassov@arm.com Cc: Russell King linux@armlinux.org.uk Cc: Hans de Goede hdegoede@redhat.com --- drivers/gpu/drm/arm/display/komeda/komeda_kms.c | 2 ++ drivers/gpu/drm/armada/armada_drv.c | 2 ++ drivers/gpu/drm/vboxvideo/vbox_drv.c | 2 ++ 3 files changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index 442d4656150a..16dfd5cdb66c 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -14,6 +14,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_managed.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h>
@@ -271,6 +272,7 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev); if (err) goto free_kms; + drmm_add_final_kfree(drm, kms);
drm->dev_private = mdev;
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 197dca3fc84c..dd9ed71ed942 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -12,6 +12,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_prime.h> #include <drm/drm_probe_helper.h> #include <drm/drm_fb_helper.h> @@ -103,6 +104,7 @@ static int armada_drm_bind(struct device *dev) kfree(priv); return ret; } + drmm_add_final_kfree(&priv->drm, priv);
/* Remove early framebuffers */ ret = drm_fb_helper_remove_conflicting_framebuffers(NULL, diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 8512d970a09f..13eaae7921f5 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -17,6 +17,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h>
#include "vbox_drv.h"
@@ -54,6 +55,7 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vbox->ddev.pdev = pdev; vbox->ddev.dev_private = vbox; pci_set_drvdata(pdev, vbox); + drmm_add_final_kfree(&vbox->ddev, vbox); mutex_init(&vbox->hw_mutex);
ret = pci_enable_device(pdev);
On Thu, Feb 27, 2020 at 07:14:49PM +0100, Daniel Vetter wrote:
These are the leftover drivers that didn't have a ->release hook that needed to be updated.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: "James (Qian) Wang" james.qian.wang@arm.com Cc: Liviu Dudau liviu.dudau@arm.com Cc: Mihail Atanassov mihail.atanassov@arm.com Cc: Russell King linux@armlinux.org.uk Cc: Hans de Goede hdegoede@redhat.com
drivers/gpu/drm/arm/display/komeda/komeda_kms.c | 2 ++ drivers/gpu/drm/armada/armada_drv.c | 2 ++ drivers/gpu/drm/vboxvideo/vbox_drv.c | 2 ++ 3 files changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index 442d4656150a..16dfd5cdb66c 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -14,6 +14,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_managed.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h>
@@ -271,6 +272,7 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev); if (err) goto free_kms;
drmm_add_final_kfree(drm, kms);
drm->dev_private = mdev;
For komeda: Acked-by: Liviu Dudau liviu.dudau@arm.com
Best regards, Liviu
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 197dca3fc84c..dd9ed71ed942 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -12,6 +12,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h> #include <drm/drm_prime.h> #include <drm/drm_probe_helper.h> #include <drm/drm_fb_helper.h> @@ -103,6 +104,7 @@ static int armada_drm_bind(struct device *dev) kfree(priv); return ret; }
drmm_add_final_kfree(&priv->drm, priv);
/* Remove early framebuffers */ ret = drm_fb_helper_remove_conflicting_framebuffers(NULL,
diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 8512d970a09f..13eaae7921f5 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -17,6 +17,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_ioctl.h> +#include <drm/drm_managed.h>
#include "vbox_drv.h"
@@ -54,6 +55,7 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vbox->ddev.pdev = pdev; vbox->ddev.dev_private = vbox; pci_set_drvdata(pdev, vbox);
drmm_add_final_kfree(&vbox->ddev, vbox); mutex_init(&vbox->hw_mutex);
ret = pci_enable_device(pdev);
-- 2.24.1
A few things: - Update the example driver in the documentation. - We can drop the old kfree in drm_dev_release. - Add a WARN_ON check in drm_dev_register to make sure everyone calls drmm_add_final_kfree and there's no leaks.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_drv.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9e62e28bbc62..1ee606b4a4f9 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -297,8 +297,6 @@ void drm_minor_release(struct drm_minor *minor) * * drm_mode_config_cleanup(drm); * drm_dev_fini(drm); - * kfree(priv->userspace_facing); - * kfree(priv); * } * * static struct drm_driver driver_drm_driver = { @@ -326,10 +324,11 @@ void drm_minor_release(struct drm_minor *minor) * kfree(drm); * return ret; * } + * drmm_add_final_kfree(drm, priv); * * drm_mode_config_init(drm); * - * priv->userspace_facing = kzalloc(..., GFP_KERNEL); + * priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL); * if (!priv->userspace_facing) * return -ENOMEM; * @@ -834,10 +833,6 @@ static void drm_dev_release(struct kref *ref) dev->driver->release(dev); } else { drm_dev_fini(dev); - if (!dev->managed.final_kfree) { - WARN_ON(!list_empty(&dev->managed.resources)); - kfree(dev); - } }
drm_managed_release(dev); @@ -960,6 +955,8 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) struct drm_driver *driver = dev->driver; int ret;
+ WARN_ON(!dev->managed.final_kfree); + if (drm_dev_needs_global_mutex(dev)) mutex_lock(&drm_global_mutex);
We need to add a drmm_kstrdup for this, but let's start somewhere.
This is not exactly perfect onion unwinding, but it's jsut a kfree so doesn't really matter at all.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_drv.c | 5 ++--- drivers/gpu/drm/drm_managed.c | 16 ++++++++++++++++ include/drm/drm_managed.h | 1 + 3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 1ee606b4a4f9..782fd5d6f8b2 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -777,7 +777,6 @@ void drm_dev_fini(struct drm_device *dev) mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->struct_mutex); drm_legacy_destroy_members(dev); - kfree(dev->unique); } EXPORT_SYMBOL(drm_dev_fini);
@@ -1063,8 +1062,8 @@ EXPORT_SYMBOL(drm_dev_unregister); */ int drm_dev_set_unique(struct drm_device *dev, const char *name) { - kfree(dev->unique); - dev->unique = kstrdup(name, GFP_KERNEL); + drmm_kfree(dev, dev->unique); + dev->unique = drmm_kstrdup(dev, name, GFP_KERNEL);
return dev->unique ? 0 : -ENOMEM; } diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index a36d4604ee18..cc917187a723 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -149,6 +149,22 @@ void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) } EXPORT_SYMBOL(drmm_kmalloc);
+char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp) +{ + size_t size; + char *buf; + + if (!s) + return NULL; + + size = strlen(s) + 1; + buf = drmm_kmalloc(dev, size, gfp); + if (buf) + memcpy(buf, s, size); + return buf; +} +EXPORT_SYMBOL_GPL(drmm_kstrdup); + void drmm_kfree(struct drm_device *dev, void *data) { struct drmres *dr_match = NULL, *dr; diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 7b5df7d09b19..89e6fce9f689 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -24,6 +24,7 @@ static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp) { return drmm_kmalloc(dev, size, gfp | __GFP_ZERO); } +char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp);
void drmm_kfree(struct drm_device *dev, void *data);
Well for the simple stuff at least, vblank, gem and minor cleanup I want to further split up as a demonstration.
v2: We need to clear drm_device->dev otherwise the debug drm printing after our cleanup hook (e.g. in drm_manged_release) will chase released memory and result in a use-after-free. Not really pretty, but oh well.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_drv.c | 48 ++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 782fd5d6f8b2..1f7ab88d9435 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -580,6 +580,23 @@ static void drm_fs_inode_free(struct inode *inode) * used. */
+static void drm_dev_init_release(struct drm_device *dev, void *res) +{ + drm_legacy_ctxbitmap_cleanup(dev); + drm_legacy_remove_map_hash(dev); + drm_fs_inode_free(dev->anon_inode); + + put_device(dev->dev); + /* Prevent use-after-free in drm_managed_release when debugging is + * enabled. Slightly awkward, but can't really be helped. */ + dev->dev = NULL; + mutex_destroy(&dev->master_mutex); + mutex_destroy(&dev->clientlist_mutex); + mutex_destroy(&dev->filelist_mutex); + mutex_destroy(&dev->struct_mutex); + drm_legacy_destroy_members(dev); +} + /** * drm_dev_init - Initialise new DRM device * @dev: DRM device @@ -647,11 +664,15 @@ int drm_dev_init(struct drm_device *dev, mutex_init(&dev->clientlist_mutex); mutex_init(&dev->master_mutex);
+ ret = drmm_add_action(dev, drm_dev_init_release, NULL); + if (ret) + return ret; + dev->anon_inode = drm_fs_inode_new(); if (IS_ERR(dev->anon_inode)) { ret = PTR_ERR(dev->anon_inode); DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret); - goto err_free; + goto err; }
if (drm_core_check_feature(dev, DRIVER_RENDER)) { @@ -688,19 +709,12 @@ int drm_dev_init(struct drm_device *dev, if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_destroy(dev); err_ctxbitmap: - drm_legacy_ctxbitmap_cleanup(dev); - drm_legacy_remove_map_hash(dev); err_minors: drm_minor_free(dev, DRM_MINOR_PRIMARY); drm_minor_free(dev, DRM_MINOR_RENDER); - drm_fs_inode_free(dev->anon_inode); -err_free: - put_device(dev->dev); - mutex_destroy(&dev->master_mutex); - mutex_destroy(&dev->clientlist_mutex); - mutex_destroy(&dev->filelist_mutex); - mutex_destroy(&dev->struct_mutex); - drm_legacy_destroy_members(dev); +err: + drm_managed_release(dev); + return ret; } EXPORT_SYMBOL(drm_dev_init); @@ -763,20 +777,8 @@ void drm_dev_fini(struct drm_device *dev) if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_destroy(dev);
- drm_legacy_ctxbitmap_cleanup(dev); - drm_legacy_remove_map_hash(dev); - drm_fs_inode_free(dev->anon_inode); - drm_minor_free(dev, DRM_MINOR_PRIMARY); drm_minor_free(dev, DRM_MINOR_RENDER); - - put_device(dev->dev); - - mutex_destroy(&dev->master_mutex); - mutex_destroy(&dev->clientlist_mutex); - mutex_destroy(&dev->filelist_mutex); - mutex_destroy(&dev->struct_mutex); - drm_legacy_destroy_members(dev); } EXPORT_SYMBOL(drm_dev_fini);
The cleanup here is somewhat tricky, since we can't tell apart the allocated minor index from 0. So register a cleanup action first, and if the index allocation fails, unregister that cleanup action again to avoid bad mistakes.
The kdev for the minor already handles NULL, so no problem there.
Hence add drmm_remove_action() to the drm_managed library.
v2: Make pointer math around void ** consistent with what Laurent suggested.
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_drv.c | 74 +++++++++++++---------------------- drivers/gpu/drm/drm_managed.c | 28 +++++++++++++ include/drm/drm_managed.h | 4 ++ 3 files changed, 59 insertions(+), 47 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 1f7ab88d9435..03a1fb377830 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -93,19 +93,35 @@ static struct drm_minor **drm_minor_get_slot(struct drm_device *dev, } }
+static void drm_minor_alloc_release(struct drm_device *dev, void *data) +{ + struct drm_minor *minor = data; + unsigned long flags; + + put_device(minor->kdev); + + spin_lock_irqsave(&drm_minor_lock, flags); + idr_remove(&drm_minors_idr, minor->index); + spin_unlock_irqrestore(&drm_minor_lock, flags); +} + static int drm_minor_alloc(struct drm_device *dev, unsigned int type) { struct drm_minor *minor; unsigned long flags; int r;
- minor = kzalloc(sizeof(*minor), GFP_KERNEL); + minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL); if (!minor) return -ENOMEM;
minor->type = type; minor->dev = dev;
+ r = drmm_add_action(dev, drm_minor_alloc_release, minor); + if (r) + return r; + idr_preload(GFP_KERNEL); spin_lock_irqsave(&drm_minor_lock, flags); r = idr_alloc(&drm_minors_idr, @@ -116,47 +132,18 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned int type) spin_unlock_irqrestore(&drm_minor_lock, flags); idr_preload_end();
- if (r < 0) - goto err_free; + if (r < 0) { + drmm_remove_action(dev, drm_minor_alloc_release, minor); + return r; + }
minor->index = r; - minor->kdev = drm_sysfs_minor_alloc(minor); - if (IS_ERR(minor->kdev)) { - r = PTR_ERR(minor->kdev); - goto err_index; - } + if (IS_ERR(minor->kdev)) + return PTR_ERR(minor->kdev);
*drm_minor_get_slot(dev, type) = minor; return 0; - -err_index: - spin_lock_irqsave(&drm_minor_lock, flags); - idr_remove(&drm_minors_idr, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); -err_free: - kfree(minor); - return r; -} - -static void drm_minor_free(struct drm_device *dev, unsigned int type) -{ - struct drm_minor **slot, *minor; - unsigned long flags; - - slot = drm_minor_get_slot(dev, type); - minor = *slot; - if (!minor) - return; - - put_device(minor->kdev); - - spin_lock_irqsave(&drm_minor_lock, flags); - idr_remove(&drm_minors_idr, minor->index); - spin_unlock_irqrestore(&drm_minor_lock, flags); - - kfree(minor); - *slot = NULL; }
static int drm_minor_register(struct drm_device *dev, unsigned int type) @@ -678,16 +665,16 @@ int drm_dev_init(struct drm_device *dev, if (drm_core_check_feature(dev, DRIVER_RENDER)) { ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); if (ret) - goto err_minors; + goto err; }
ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY); if (ret) - goto err_minors; + goto err;
ret = drm_legacy_create_map_hash(dev); if (ret) - goto err_minors; + goto err;
drm_legacy_ctxbitmap_init(dev);
@@ -695,7 +682,7 @@ int drm_dev_init(struct drm_device *dev, ret = drm_gem_init(dev); if (ret) { DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n"); - goto err_ctxbitmap; + goto err; } }
@@ -708,10 +695,6 @@ int drm_dev_init(struct drm_device *dev, err_setunique: if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_destroy(dev); -err_ctxbitmap: -err_minors: - drm_minor_free(dev, DRM_MINOR_PRIMARY); - drm_minor_free(dev, DRM_MINOR_RENDER); err: drm_managed_release(dev);
@@ -776,9 +759,6 @@ void drm_dev_fini(struct drm_device *dev)
if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_destroy(dev); - - drm_minor_free(dev, DRM_MINOR_PRIMARY); - drm_minor_free(dev, DRM_MINOR_RENDER); } EXPORT_SYMBOL(drm_dev_fini);
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index cc917187a723..626656369f0b 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -134,6 +134,34 @@ int __drmm_add_action(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action);
+void drmm_remove_action(struct drm_device *dev, + drmres_release_t action, + void *data) +{ + struct drmres *dr = NULL, *tmp; + unsigned long flags; + + if (!data) + return; + + spin_lock_irqsave(&dev->managed.lock, flags); + list_for_each_entry(tmp, &dev->managed.resources, node.entry) { + if (tmp->node.release == action && + *(void **)&tmp->data == data) { + dr = tmp; + del_dr(dev, dr); + break; + } + } + spin_unlock_irqrestore(&dev->managed.lock, flags); + + if (WARN_ON(!dr)) + return; + + kfree(dr); +} +EXPORT_SYMBOL(drmm_remove_action); + void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) { struct drmres *dr; diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 89e6fce9f689..5280209dff92 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -17,6 +17,10 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+void drmm_remove_action(struct drm_device *dev, + drmres_release_t action, + void *data); + void drmm_add_final_kfree(struct drm_device *dev, void *parent);
void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc;
We might want to look into pushing this down into drm_mm_init, but that would mean rolling out return codes to a pile of functions unfortunately. So let's leave that for now.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_drv.c | 8 +------- drivers/gpu/drm/drm_gem.c | 21 ++++++++++----------- drivers/gpu/drm/drm_internal.h | 1 - 3 files changed, 11 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 03a1fb377830..7b3df1188da9 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -688,13 +688,10 @@ int drm_dev_init(struct drm_device *dev,
ret = drm_dev_set_unique(dev, dev_name(parent)); if (ret) - goto err_setunique; + goto err;
return 0;
-err_setunique: - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_destroy(dev); err: drm_managed_release(dev);
@@ -756,9 +753,6 @@ EXPORT_SYMBOL(devm_drm_dev_init); void drm_dev_fini(struct drm_device *dev) { drm_vblank_cleanup(dev); - - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_destroy(dev); } EXPORT_SYMBOL(drm_dev_fini);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 0b6e6623735e..31095e0f6b9f 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -44,6 +44,7 @@ #include <drm/drm_drv.h> #include <drm/drm_file.h> #include <drm/drm_gem.h> +#include <drm/drm_managed.h> #include <drm/drm_print.h> #include <drm/drm_vma_manager.h>
@@ -77,6 +78,12 @@ * up at a later date, and as our interface with shmfs for memory allocation. */
+static void +drm_gem_init_release(struct drm_device *dev, void *ptr) +{ + drm_vma_offset_manager_destroy(dev->vma_offset_manager); +} + /** * drm_gem_init - Initialize the GEM device fields * @dev: drm_devic structure to initialize @@ -89,7 +96,8 @@ drm_gem_init(struct drm_device *dev) mutex_init(&dev->object_name_lock); idr_init_base(&dev->object_name_idr, 1);
- vma_offset_manager = kzalloc(sizeof(*vma_offset_manager), GFP_KERNEL); + vma_offset_manager = drmm_kzalloc(dev, sizeof(*vma_offset_manager), + GFP_KERNEL); if (!vma_offset_manager) { DRM_ERROR("out of memory\n"); return -ENOMEM; @@ -100,16 +108,7 @@ drm_gem_init(struct drm_device *dev) DRM_FILE_PAGE_OFFSET_START, DRM_FILE_PAGE_OFFSET_SIZE);
- return 0; -} - -void -drm_gem_destroy(struct drm_device *dev) -{ - - drm_vma_offset_manager_destroy(dev->vma_offset_manager); - kfree(dev->vma_offset_manager); - dev->vma_offset_manager = NULL; + return drmm_add_action(dev, drm_gem_init_release, NULL); }
/** diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 8c2628dfc6c7..cb09e95a795e 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -144,7 +144,6 @@ void drm_sysfs_lease_event(struct drm_device *dev); /* drm_gem.c */ struct drm_gem_object; int drm_gem_init(struct drm_device *dev); -void drm_gem_destroy(struct drm_device *dev); int drm_gem_handle_create_tail(struct drm_file *file_priv, struct drm_gem_object *obj, u32 *handlep);
Nothing special here, except that this is the first time that we automatically clean up something that's initialized with an explicit driver call. But the cleanup was done at the very of the release sequence for all drivers, and that's still the case. At least without more uses of drmm_ through explicit driver calls.
Also for this one we need drmm_kcalloc, so lets add those
v2: Sort includes (Laurent)
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_drv.c | 1 - drivers/gpu/drm/drm_internal.h | 1 - drivers/gpu/drm/drm_vblank.c | 31 ++++++++++++------------------- include/drm/drm_managed.h | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 7b3df1188da9..b8db2cc4a19b 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -752,7 +752,6 @@ EXPORT_SYMBOL(devm_drm_dev_init); */ void drm_dev_fini(struct drm_device *dev) { - drm_vblank_cleanup(dev); } EXPORT_SYMBOL(drm_dev_fini);
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index cb09e95a795e..e67015d07c4c 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -94,7 +94,6 @@ void drm_managed_release(struct drm_device *dev);
/* drm_vblank.c */ void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); -void drm_vblank_cleanup(struct drm_device *dev);
/* IOCTLS */ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 47fc4339ec7f..5a6ec8aa0873 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -30,6 +30,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_drv.h> #include <drm/drm_framebuffer.h> +#include <drm/drm_managed.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_print.h> #include <drm/drm_vblank.h> @@ -425,14 +426,10 @@ static void vblank_disable_fn(struct timer_list *t) spin_unlock_irqrestore(&dev->vbl_lock, irqflags); }
-void drm_vblank_cleanup(struct drm_device *dev) +static void drm_vblank_init_release(struct drm_device *dev, void *ptr) { unsigned int pipe;
- /* Bail if the driver didn't call drm_vblank_init() */ - if (dev->num_crtcs == 0) - return; - for (pipe = 0; pipe < dev->num_crtcs; pipe++) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
@@ -441,10 +438,6 @@ void drm_vblank_cleanup(struct drm_device *dev)
del_timer_sync(&vblank->disable_timer); } - - kfree(dev->vblank); - - dev->num_crtcs = 0; }
/** @@ -453,25 +446,29 @@ void drm_vblank_cleanup(struct drm_device *dev) * @num_crtcs: number of CRTCs supported by @dev * * This function initializes vblank support for @num_crtcs display pipelines. - * Cleanup is handled by the DRM core, or through calling drm_dev_fini() for - * drivers with a &drm_driver.release callback. + * Cleanup is handled automatically through a cleanup function added with + * drmm_add_action(). * * Returns: * Zero on success or a negative error code on failure. */ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) { - int ret = -ENOMEM; + int ret; unsigned int i;
spin_lock_init(&dev->vbl_lock); spin_lock_init(&dev->vblank_time_lock);
+ dev->vblank = drmm_kcalloc(dev, num_crtcs, sizeof(*dev->vblank), GFP_KERNEL); + if (!dev->vblank) + return -ENOMEM; + dev->num_crtcs = num_crtcs;
- dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL); - if (!dev->vblank) - goto err; + ret = drmm_add_action(dev, drm_vblank_init_release, NULL); + if (ret) + return ret;
for (i = 0; i < num_crtcs; i++) { struct drm_vblank_crtc *vblank = &dev->vblank[i]; @@ -486,10 +483,6 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
return 0; - -err: - dev->num_crtcs = 0; - return ret; } EXPORT_SYMBOL(drm_vblank_init);
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 5280209dff92..2b1ba2ad5582 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -4,6 +4,7 @@ #define _DRM_MANAGED_H_
#include <linux/gfp.h> +#include <linux/overflow.h> #include <linux/types.h>
struct drm_device; @@ -28,6 +29,21 @@ static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp) { return drmm_kmalloc(dev, size, gfp | __GFP_ZERO); } +static inline void *drmm_kmalloc_array(struct drm_device *dev, + size_t n, size_t size, gfp_t flags) +{ + size_t bytes; + + if (unlikely(check_mul_overflow(n, size, &bytes))) + return NULL; + + return drmm_kmalloc(dev, bytes, flags); +} +static inline void *drmm_kcalloc(struct drm_device *dev, + size_t n, size_t size, gfp_t flags) +{ + return drmm_kmalloc_array(dev, n, size, flags | __GFP_ZERO); +} char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp);
void drmm_kfree(struct drm_device *dev, void *data);
It has become empty. Given the few users I figured not much point splitting this up.
v2: Rebase over i915 changes.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/cirrus/cirrus.c | 1 - drivers/gpu/drm/drm_drv.c | 23 +------------------ drivers/gpu/drm/drm_mipi_dbi.c | 1 - drivers/gpu/drm/i915/i915_drv.c | 9 -------- .../gpu/drm/i915/selftests/mock_gem_device.c | 2 -- drivers/gpu/drm/ingenic/ingenic-drm.c | 1 - drivers/gpu/drm/mcde/mcde_drv.c | 1 - drivers/gpu/drm/tidss/tidss_drv.c | 2 -- drivers/gpu/drm/tiny/gm12u320.c | 1 - drivers/gpu/drm/tiny/repaper.c | 1 - drivers/gpu/drm/udl/udl_drv.c | 1 - drivers/gpu/drm/vgem/vgem_drv.c | 1 - drivers/gpu/drm/vkms/vkms_drv.c | 1 - drivers/gpu/drm/xen/xen_drm_front.c | 2 -- include/drm/drm_drv.h | 5 +--- 15 files changed, 2 insertions(+), 50 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index 2232556ce34c..a9d789a56536 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c @@ -529,7 +529,6 @@ static void cirrus_mode_config_init(struct cirrus_device *cirrus) static void cirrus_release(struct drm_device *dev) { drm_mode_config_cleanup(dev); - drm_dev_fini(dev); }
DEFINE_DRM_GEM_FOPS(cirrus_fops); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index b8db2cc4a19b..3cf40864d4a6 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -283,7 +283,6 @@ void drm_minor_release(struct drm_minor *minor) * struct driver_device *priv = container_of(...); * * drm_mode_config_cleanup(drm); - * drm_dev_fini(drm); * } * * static struct drm_driver driver_drm_driver = { @@ -738,23 +737,6 @@ int devm_drm_dev_init(struct device *parent, } EXPORT_SYMBOL(devm_drm_dev_init);
-/** - * drm_dev_fini - Finalize a dead DRM device - * @dev: DRM device - * - * Finalize a dead DRM device. This is the converse to drm_dev_init() and - * frees up all data allocated by it. All driver private data should be - * finalized first. Note that this function does not free the @dev, that is - * left to the caller. - * - * The ref-count of @dev must be zero, and drm_dev_fini() should only be called - * from a &drm_driver.release callback. - */ -void drm_dev_fini(struct drm_device *dev) -{ -} -EXPORT_SYMBOL(drm_dev_fini); - /** * drm_dev_alloc - Allocate new DRM device * @driver: DRM driver to allocate device for @@ -803,11 +785,8 @@ static void drm_dev_release(struct kref *ref) { struct drm_device *dev = container_of(ref, struct drm_device, ref);
- if (dev->driver->release) { + if (dev->driver->release) dev->driver->release(dev); - } else { - drm_dev_fini(dev); - }
drm_managed_release(dev);
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 069603dfcd10..a678e07508d4 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -591,7 +591,6 @@ void mipi_dbi_release(struct drm_device *drm) DRM_DEBUG_DRIVER("\n");
drm_mode_config_cleanup(drm); - drm_dev_fini(drm); } EXPORT_SYMBOL(mipi_dbi_release);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 4119e57b0c5b..64dfaefac4d1 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -910,13 +910,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) return i915; }
-static void i915_driver_destroy(struct drm_i915_private *i915) -{ - struct pci_dev *pdev = i915->drm.pdev; - - drm_dev_fini(&i915->drm); -} - /** * i915_driver_probe - setup chip and create an initial config * @pdev: PCI device @@ -1019,7 +1012,6 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_disable_device(pdev); out_fini: i915_probe_error(i915, "Device initialization failed (%d)\n", ret); - i915_driver_destroy(i915); drm_dev_put(&i915->drm); return ret; } @@ -1076,7 +1068,6 @@ static void i915_driver_release(struct drm_device *dev) intel_runtime_pm_driver_release(rpm);
i915_driver_late_release(dev_priv); - i915_driver_destroy(dev_priv); }
static int i915_driver_open(struct drm_device *dev, struct drm_file *file) diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c index c85bbc88f504..3435b9c62366 100644 --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c @@ -76,7 +76,6 @@ static void mock_device_release(struct drm_device *dev)
drm_mode_config_cleanup(&i915->drm);
- drm_dev_fini(&i915->drm); out: put_device(&i915->drm.pdev->dev); kfree(i915->drm.pdev); @@ -216,7 +215,6 @@ struct drm_i915_private *mock_gem_device(void) intel_gt_driver_late_release(&i915->gt); intel_memory_regions_driver_release(i915); drm_mode_config_cleanup(&i915->drm); - drm_dev_fini(&i915->drm); drm_dev_put(&i915->drm);
return NULL; diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index e2c832eb4e9a..192aaa4421a3 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -492,7 +492,6 @@ static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) static void ingenic_drm_release(struct drm_device *drm) { drm_mode_config_cleanup(drm); - drm_dev_fini(drm); }
static int ingenic_drm_enable_vblank(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 7474481503a1..a543ebf3d541 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -223,7 +223,6 @@ static void mcde_release(struct drm_device *drm) struct mcde *mcde = drm->dev_private;
drm_mode_config_cleanup(drm); - drm_dev_fini(drm); }
DEFINE_DRM_GEM_CMA_FOPS(drm_fops); diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index 32a85628dbec..460d5e9d0cf4 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -108,8 +108,6 @@ static void tidss_release(struct drm_device *ddev) drm_kms_helper_poll_fini(ddev);
tidss_modeset_cleanup(tidss); - - drm_dev_fini(ddev); }
DEFINE_DRM_GEM_CMA_FOPS(tidss_fops); diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 524ca0941cf9..3928f69bbd3d 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -637,7 +637,6 @@ static void gm12u320_driver_release(struct drm_device *dev)
gm12u320_usb_free(gm12u320); drm_mode_config_cleanup(dev); - drm_dev_fini(dev); }
DEFINE_DRM_GEM_FOPS(gm12u320_fops); diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index df5654ef53ee..4741ff670ec9 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -914,7 +914,6 @@ static void repaper_release(struct drm_device *drm) DRM_DEBUG_DRIVER("\n");
drm_mode_config_cleanup(drm); - drm_dev_fini(drm); }
static const uint32_t repaper_formats[] = { diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 6a5594946096..8b78c356beb5 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -38,7 +38,6 @@ static void udl_driver_release(struct drm_device *dev) { udl_fini(dev); udl_modeset_cleanup(dev); - drm_dev_fini(dev); }
static struct drm_driver driver = { diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 7486014e9149..ec1a8ebb6f1b 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -432,7 +432,6 @@ static void vgem_release(struct drm_device *dev) struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
platform_device_unregister(vgem->platform); - drm_dev_fini(&vgem->drm); }
static struct drm_driver vgem_driver = { diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 2f35fe789343..eef85f1a0ce5 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -64,7 +64,6 @@ static void vkms_release(struct drm_device *dev) platform_device_unregister(vkms->platform); drm_atomic_helper_shutdown(&vkms->drm); drm_mode_config_cleanup(&vkms->drm); - drm_dev_fini(&vkms->drm); destroy_workqueue(vkms->output.composer_workq); }
diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c index d22b5da38935..b91d23b5f3ae 100644 --- a/drivers/gpu/drm/xen/xen_drm_front.c +++ b/drivers/gpu/drm/xen/xen_drm_front.c @@ -460,8 +460,6 @@ static void xen_drm_drv_release(struct drm_device *dev) drm_atomic_helper_shutdown(dev); drm_mode_config_cleanup(dev);
- drm_dev_fini(dev); - if (front_info->cfg.be_alloc) xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index cbd050419ab8..edee40e31e4b 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -265,9 +265,7 @@ struct drm_driver { * @release: * * Optional callback for destroying device data after the final - * reference is released, i.e. the device is being destroyed. Drivers - * using this callback are responsible for calling drm_dev_fini() - * to finalize the device and then freeing the struct themselves. + * reference is released, i.e. the device is being destroyed. */ void (*release) (struct drm_device *);
@@ -623,7 +621,6 @@ int drm_dev_init(struct drm_device *dev, int devm_drm_dev_init(struct device *parent, struct drm_device *dev, struct drm_driver *driver); -void drm_dev_fini(struct drm_device *dev);
struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent);
drm_mode_config_cleanup is idempotent, so no harm in calling this twice. This allows us to gradually switch drivers over by removing explicit drm_mode_config_cleanup calls.
With this step it's not also possible that (at least for simple drivers) automatic resource cleanup can be done correctly without a drm_driver->release hook. Therefore allow this now in devm_drm_dev_init().
Also with drmm_ explicit drm_driver->release hooks are kinda not the best option, so deprecate that hook to discourage future users.
v2: Fixup the example in the kerneldoc too.
v3: - For paranoia, double check that minor->dev == dev in the release hook, because I botched the pointer math in the drmm library. - Call drm_mode_config_cleanup when drmm_add_action fails, we'd be missing some mutex_destroy and ida_cleanup otherwise (Laurent)
v4: Add a drmm_add_action_or_reset (like devm_ has) to encapsulate this pattern (Noralf).
v5: Fix oversight in the new add_action_or_reset macro (Noralf)
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Acked-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_drv.c | 23 +++++++---------------- drivers/gpu/drm/drm_managed.c | 14 ++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 13 ++++++++++++- include/drm/drm_managed.h | 7 +++++++ include/drm/drm_mode_config.h | 2 +- 5 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3cf40864d4a6..bb326b9bcde0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -98,6 +98,8 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) struct drm_minor *minor = data; unsigned long flags;
+ WARN_ON(dev != minor->dev); + put_device(minor->kdev);
spin_lock_irqsave(&drm_minor_lock, flags); @@ -267,8 +269,7 @@ void drm_minor_release(struct drm_minor *minor) * * The following example shows a typical structure of a DRM display driver. * The example focus on the probe() function and the other functions that is - * almost always present and serves as a demonstration of devm_drm_dev_init() - * usage with its accompanying drm_driver->release callback. + * almost always present and serves as a demonstration of devm_drm_dev_init(). * * .. code-block:: c * @@ -278,16 +279,8 @@ void drm_minor_release(struct drm_minor *minor) * struct clk *pclk; * }; * - * static void driver_drm_release(struct drm_device *drm) - * { - * struct driver_device *priv = container_of(...); - * - * drm_mode_config_cleanup(drm); - * } - * * static struct drm_driver driver_drm_driver = { * [...] - * .release = driver_drm_release, * }; * * static int driver_probe(struct platform_device *pdev) @@ -312,7 +305,9 @@ void drm_minor_release(struct drm_minor *minor) * } * drmm_add_final_kfree(drm, priv); * - * drm_mode_config_init(drm); + * ret = drm_mode_config_init(drm); + * if (ret) + * return ret; * * priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL); * if (!priv->userspace_facing) @@ -710,8 +705,7 @@ static void devm_drm_dev_init_release(void *data) * @driver: DRM driver * * Managed drm_dev_init(). The DRM device initialized with this function is - * automatically put on driver detach using drm_dev_put(). You must supply a - * &drm_driver.release callback to control the finalization explicitly. + * automatically put on driver detach using drm_dev_put(). * * RETURNS: * 0 on success, or error code on failure. @@ -722,9 +716,6 @@ int devm_drm_dev_init(struct device *parent, { int ret;
- if (WARN_ON(!driver->release)) - return -EINVAL; - ret = drm_dev_init(dev, driver, parent); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 626656369f0b..6376be01bbc8 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -134,6 +134,20 @@ int __drmm_add_action(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action);
+int __drmm_add_action_or_reset(struct drm_device *dev, + drmres_release_t action, + void *data, const char *name) +{ + int ret; + + ret = __drmm_add_action(dev, action, data, name); + if (ret) + action(dev, data); + + return ret; +} +EXPORT_SYMBOL(__drmm_add_action_or_reset); + void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 08e6eff6a179..6f7005bc597f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -25,6 +25,7 @@ #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_file.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_config.h> #include <drm/drm_print.h> #include <linux/dma-resv.h> @@ -373,6 +374,11 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return 0; }
+static void drm_mode_config_init_release(struct drm_device *dev, void *ptr) +{ + drm_mode_config_cleanup(dev); +} + /** * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device @@ -384,8 +390,10 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) * problem, since this should happen single threaded at init time. It is the * driver's problem to ensure this guarantee. * + * Cleanup is automatically handled through registering drm_mode_config_cleanup + * with drmm_add_action(). */ -void drm_mode_config_init(struct drm_device *dev) +int drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); drm_modeset_lock_init(&dev->mode_config.connection_mutex); @@ -443,6 +451,9 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_acquire_fini(&modeset_ctx); dma_resv_fini(&resv); } + + return drmm_add_action_or_reset(dev, drm_mode_config_init_release, + NULL); } EXPORT_SYMBOL(drm_mode_config_init);
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 2b1ba2ad5582..1e6291407586 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,6 +18,13 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+#define drmm_add_action_or_reset(dev, action, data) \ + __drmm_add_action_or_reset(dev, action, data, #action) + +int __must_check __drmm_add_action_or_reset(struct drm_device *dev, + drmres_release_t action, + void *data, const char *name); + void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data); diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 3bcbe30339f0..160a3e4b51c3 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -929,7 +929,7 @@ struct drm_mode_config { const struct drm_mode_config_helper_funcs *helper_private; };
-void drm_mode_config_init(struct drm_device *dev); +int drm_mode_config_init(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev);
Hi Daniel
Am 27.02.20 um 19:14 schrieb Daniel Vetter:
drm_mode_config_cleanup is idempotent, so no harm in calling this twice. This allows us to gradually switch drivers over by removing explicit drm_mode_config_cleanup calls.
With this step it's not also possible that (at least for simple drivers) automatic resource cleanup can be done correctly without a drm_driver->release hook. Therefore allow this now in devm_drm_dev_init().
Also with drmm_ explicit drm_driver->release hooks are kinda not the best option, so deprecate that hook to discourage future users.
v2: Fixup the example in the kerneldoc too.
v3:
- For paranoia, double check that minor->dev == dev in the release hook, because I botched the pointer math in the drmm library.
- Call drm_mode_config_cleanup when drmm_add_action fails, we'd be missing some mutex_destroy and ida_cleanup otherwise (Laurent)
v4: Add a drmm_add_action_or_reset (like devm_ has) to encapsulate this pattern (Noralf).
v5: Fix oversight in the new add_action_or_reset macro (Noralf)
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Acked-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_drv.c | 23 +++++++---------------- drivers/gpu/drm/drm_managed.c | 14 ++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 13 ++++++++++++- include/drm/drm_managed.h | 7 +++++++ include/drm/drm_mode_config.h | 2 +- 5 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3cf40864d4a6..bb326b9bcde0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -98,6 +98,8 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) struct drm_minor *minor = data; unsigned long flags;
WARN_ON(dev != minor->dev);
put_device(minor->kdev);
spin_lock_irqsave(&drm_minor_lock, flags);
@@ -267,8 +269,7 @@ void drm_minor_release(struct drm_minor *minor)
- The following example shows a typical structure of a DRM display driver.
- The example focus on the probe() function and the other functions that is
- almost always present and serves as a demonstration of devm_drm_dev_init()
- usage with its accompanying drm_driver->release callback.
- almost always present and serves as a demonstration of devm_drm_dev_init().
- .. code-block:: c
@@ -278,16 +279,8 @@ void drm_minor_release(struct drm_minor *minor)
struct clk *pclk;
- };
- static void driver_drm_release(struct drm_device *drm)
- {
struct driver_device *priv = container_of(...);
drm_mode_config_cleanup(drm);
- }
- static struct drm_driver driver_drm_driver = {
[...]
.release = driver_drm_release,
- };
- static int driver_probe(struct platform_device *pdev)
@@ -312,7 +305,9 @@ void drm_minor_release(struct drm_minor *minor)
}
drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm);
ret = drm_mode_config_init(drm);
if (ret)
return ret;
priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL);
if (!priv->userspace_facing)
@@ -710,8 +705,7 @@ static void devm_drm_dev_init_release(void *data)
- @driver: DRM driver
- Managed drm_dev_init(). The DRM device initialized with this function is
- automatically put on driver detach using drm_dev_put(). You must supply a
- &drm_driver.release callback to control the finalization explicitly.
- automatically put on driver detach using drm_dev_put().
- RETURNS:
- 0 on success, or error code on failure.
@@ -722,9 +716,6 @@ int devm_drm_dev_init(struct device *parent, { int ret;
- if (WARN_ON(!driver->release))
return -EINVAL;
- ret = drm_dev_init(dev, driver, parent); if (ret) return ret;
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 626656369f0b..6376be01bbc8 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -134,6 +134,20 @@ int __drmm_add_action(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action);
+int __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name)
+{
- int ret;
- ret = __drmm_add_action(dev, action, data, name);
- if (ret)
action(dev, data);
- return ret;
+} +EXPORT_SYMBOL(__drmm_add_action_or_reset);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 08e6eff6a179..6f7005bc597f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -25,6 +25,7 @@ #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_file.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_config.h> #include <drm/drm_print.h> #include <linux/dma-resv.h> @@ -373,6 +374,11 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return 0; }
+static void drm_mode_config_init_release(struct drm_device *dev, void *ptr) +{
- drm_mode_config_cleanup(dev);
+}
/**
- drm_mode_config_init - initialize DRM mode_configuration structure
- @dev: DRM device
@@ -384,8 +390,10 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
- problem, since this should happen single threaded at init time. It is the
- driver's problem to ensure this guarantee.
- Cleanup is automatically handled through registering drm_mode_config_cleanup
*/
- with drmm_add_action().
-void drm_mode_config_init(struct drm_device *dev) +int drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); drm_modeset_lock_init(&dev->mode_config.connection_mutex); @@ -443,6 +451,9 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_acquire_fini(&modeset_ctx); dma_resv_fini(&resv); }
- return drmm_add_action_or_reset(dev, drm_mode_config_init_release,
NULL);
} EXPORT_SYMBOL(drm_mode_config_init);
It might be a bit late in the review cycle, but could this function be renamed to drmm_mode_config_init() to reflect the garbage-collected implementation?
Best regards Thomas
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 2b1ba2ad5582..1e6291407586 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,6 +18,13 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+#define drmm_add_action_or_reset(dev, action, data) \
- __drmm_add_action_or_reset(dev, action, data, #action)
+int __must_check __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data); diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 3bcbe30339f0..160a3e4b51c3 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -929,7 +929,7 @@ struct drm_mode_config { const struct drm_mode_config_helper_funcs *helper_private; };
-void drm_mode_config_init(struct drm_device *dev); +int drm_mode_config_init(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev);
On Fri, Feb 28, 2020 at 8:30 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 27.02.20 um 19:14 schrieb Daniel Vetter:
drm_mode_config_cleanup is idempotent, so no harm in calling this twice. This allows us to gradually switch drivers over by removing explicit drm_mode_config_cleanup calls.
With this step it's not also possible that (at least for simple drivers) automatic resource cleanup can be done correctly without a drm_driver->release hook. Therefore allow this now in devm_drm_dev_init().
Also with drmm_ explicit drm_driver->release hooks are kinda not the best option, so deprecate that hook to discourage future users.
v2: Fixup the example in the kerneldoc too.
v3:
- For paranoia, double check that minor->dev == dev in the release hook, because I botched the pointer math in the drmm library.
- Call drm_mode_config_cleanup when drmm_add_action fails, we'd be missing some mutex_destroy and ida_cleanup otherwise (Laurent)
v4: Add a drmm_add_action_or_reset (like devm_ has) to encapsulate this pattern (Noralf).
v5: Fix oversight in the new add_action_or_reset macro (Noralf)
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Acked-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_drv.c | 23 +++++++---------------- drivers/gpu/drm/drm_managed.c | 14 ++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 13 ++++++++++++- include/drm/drm_managed.h | 7 +++++++ include/drm/drm_mode_config.h | 2 +- 5 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3cf40864d4a6..bb326b9bcde0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -98,6 +98,8 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) struct drm_minor *minor = data; unsigned long flags;
WARN_ON(dev != minor->dev);
put_device(minor->kdev); spin_lock_irqsave(&drm_minor_lock, flags);
@@ -267,8 +269,7 @@ void drm_minor_release(struct drm_minor *minor)
- The following example shows a typical structure of a DRM display driver.
- The example focus on the probe() function and the other functions that is
- almost always present and serves as a demonstration of devm_drm_dev_init()
- usage with its accompanying drm_driver->release callback.
- almost always present and serves as a demonstration of devm_drm_dev_init().
- .. code-block:: c
@@ -278,16 +279,8 @@ void drm_minor_release(struct drm_minor *minor)
struct clk *pclk;
- };
- static void driver_drm_release(struct drm_device *drm)
- {
struct driver_device *priv = container_of(...);
drm_mode_config_cleanup(drm);
- }
- static struct drm_driver driver_drm_driver = {
[...]
.release = driver_drm_release,
- };
- static int driver_probe(struct platform_device *pdev)
@@ -312,7 +305,9 @@ void drm_minor_release(struct drm_minor *minor)
}
drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm);
ret = drm_mode_config_init(drm);
if (ret)
return ret;
priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL);
if (!priv->userspace_facing)
@@ -710,8 +705,7 @@ static void devm_drm_dev_init_release(void *data)
- @driver: DRM driver
- Managed drm_dev_init(). The DRM device initialized with this function is
- automatically put on driver detach using drm_dev_put(). You must supply a
- &drm_driver.release callback to control the finalization explicitly.
- automatically put on driver detach using drm_dev_put().
- RETURNS:
- 0 on success, or error code on failure.
@@ -722,9 +716,6 @@ int devm_drm_dev_init(struct device *parent, { int ret;
if (WARN_ON(!driver->release))
return -EINVAL;
ret = drm_dev_init(dev, driver, parent); if (ret) return ret;
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 626656369f0b..6376be01bbc8 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -134,6 +134,20 @@ int __drmm_add_action(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action);
+int __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name)
+{
int ret;
ret = __drmm_add_action(dev, action, data, name);
if (ret)
action(dev, data);
return ret;
+} +EXPORT_SYMBOL(__drmm_add_action_or_reset);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 08e6eff6a179..6f7005bc597f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -25,6 +25,7 @@ #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_file.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_config.h> #include <drm/drm_print.h> #include <linux/dma-resv.h> @@ -373,6 +374,11 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return 0; }
+static void drm_mode_config_init_release(struct drm_device *dev, void *ptr) +{
drm_mode_config_cleanup(dev);
+}
/**
- drm_mode_config_init - initialize DRM mode_configuration structure
- @dev: DRM device
@@ -384,8 +390,10 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
- problem, since this should happen single threaded at init time. It is the
- driver's problem to ensure this guarantee.
- Cleanup is automatically handled through registering drm_mode_config_cleanup
*/
- with drmm_add_action().
-void drm_mode_config_init(struct drm_device *dev) +int drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); drm_modeset_lock_init(&dev->mode_config.connection_mutex); @@ -443,6 +451,9 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_acquire_fini(&modeset_ctx); dma_resv_fini(&resv); }
return drmm_add_action_or_reset(dev, drm_mode_config_init_release,
NULL);
} EXPORT_SYMBOL(drm_mode_config_init);
It might be a bit late in the review cycle, but could this function be renamed to drmm_mode_config_init() to reflect the garbage-collected implementation?
I think that makes sense, at least as a wrapper with a __must_check annotation (and keeping the old one). I tried to avoid the rename because it'd be a flag day patch.
If you think this would be a great improvement then I can do that - I'm positive enough to bother with the rebase to touch up all the driver patches, but only if there's someone else who's enthusiastic about it :-) -Daniel
Best regards Thomas
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 2b1ba2ad5582..1e6291407586 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,6 +18,13 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+#define drmm_add_action_or_reset(dev, action, data) \
__drmm_add_action_or_reset(dev, action, data, #action)
+int __must_check __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data); diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 3bcbe30339f0..160a3e4b51c3 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -929,7 +929,7 @@ struct drm_mode_config { const struct drm_mode_config_helper_funcs *helper_private; };
-void drm_mode_config_init(struct drm_device *dev); +int drm_mode_config_init(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev);
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
Hi
Am 28.02.20 um 09:43 schrieb Daniel Vetter:
On Fri, Feb 28, 2020 at 8:30 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 27.02.20 um 19:14 schrieb Daniel Vetter:
drm_mode_config_cleanup is idempotent, so no harm in calling this twice. This allows us to gradually switch drivers over by removing explicit drm_mode_config_cleanup calls.
With this step it's not also possible that (at least for simple drivers) automatic resource cleanup can be done correctly without a drm_driver->release hook. Therefore allow this now in devm_drm_dev_init().
Also with drmm_ explicit drm_driver->release hooks are kinda not the best option, so deprecate that hook to discourage future users.
v2: Fixup the example in the kerneldoc too.
v3:
- For paranoia, double check that minor->dev == dev in the release hook, because I botched the pointer math in the drmm library.
- Call drm_mode_config_cleanup when drmm_add_action fails, we'd be missing some mutex_destroy and ida_cleanup otherwise (Laurent)
v4: Add a drmm_add_action_or_reset (like devm_ has) to encapsulate this pattern (Noralf).
v5: Fix oversight in the new add_action_or_reset macro (Noralf)
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Acked-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_drv.c | 23 +++++++---------------- drivers/gpu/drm/drm_managed.c | 14 ++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 13 ++++++++++++- include/drm/drm_managed.h | 7 +++++++ include/drm/drm_mode_config.h | 2 +- 5 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3cf40864d4a6..bb326b9bcde0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -98,6 +98,8 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) struct drm_minor *minor = data; unsigned long flags;
WARN_ON(dev != minor->dev);
put_device(minor->kdev); spin_lock_irqsave(&drm_minor_lock, flags);
@@ -267,8 +269,7 @@ void drm_minor_release(struct drm_minor *minor)
- The following example shows a typical structure of a DRM display driver.
- The example focus on the probe() function and the other functions that is
- almost always present and serves as a demonstration of devm_drm_dev_init()
- usage with its accompanying drm_driver->release callback.
- almost always present and serves as a demonstration of devm_drm_dev_init().
- .. code-block:: c
@@ -278,16 +279,8 @@ void drm_minor_release(struct drm_minor *minor)
struct clk *pclk;
- };
- static void driver_drm_release(struct drm_device *drm)
- {
struct driver_device *priv = container_of(...);
drm_mode_config_cleanup(drm);
- }
- static struct drm_driver driver_drm_driver = {
[...]
.release = driver_drm_release,
- };
- static int driver_probe(struct platform_device *pdev)
@@ -312,7 +305,9 @@ void drm_minor_release(struct drm_minor *minor)
}
drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm);
ret = drm_mode_config_init(drm);
if (ret)
return ret;
priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL);
if (!priv->userspace_facing)
@@ -710,8 +705,7 @@ static void devm_drm_dev_init_release(void *data)
- @driver: DRM driver
- Managed drm_dev_init(). The DRM device initialized with this function is
- automatically put on driver detach using drm_dev_put(). You must supply a
- &drm_driver.release callback to control the finalization explicitly.
- automatically put on driver detach using drm_dev_put().
- RETURNS:
- 0 on success, or error code on failure.
@@ -722,9 +716,6 @@ int devm_drm_dev_init(struct device *parent, { int ret;
if (WARN_ON(!driver->release))
return -EINVAL;
ret = drm_dev_init(dev, driver, parent); if (ret) return ret;
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 626656369f0b..6376be01bbc8 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -134,6 +134,20 @@ int __drmm_add_action(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action);
+int __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name)
+{
int ret;
ret = __drmm_add_action(dev, action, data, name);
if (ret)
action(dev, data);
return ret;
+} +EXPORT_SYMBOL(__drmm_add_action_or_reset);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 08e6eff6a179..6f7005bc597f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -25,6 +25,7 @@ #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_file.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_config.h> #include <drm/drm_print.h> #include <linux/dma-resv.h> @@ -373,6 +374,11 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return 0; }
+static void drm_mode_config_init_release(struct drm_device *dev, void *ptr) +{
drm_mode_config_cleanup(dev);
+}
/**
- drm_mode_config_init - initialize DRM mode_configuration structure
- @dev: DRM device
@@ -384,8 +390,10 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
- problem, since this should happen single threaded at init time. It is the
- driver's problem to ensure this guarantee.
- Cleanup is automatically handled through registering drm_mode_config_cleanup
*/
- with drmm_add_action().
-void drm_mode_config_init(struct drm_device *dev) +int drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); drm_modeset_lock_init(&dev->mode_config.connection_mutex); @@ -443,6 +451,9 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_acquire_fini(&modeset_ctx); dma_resv_fini(&resv); }
return drmm_add_action_or_reset(dev, drm_mode_config_init_release,
NULL);
} EXPORT_SYMBOL(drm_mode_config_init);
It might be a bit late in the review cycle, but could this function be renamed to drmm_mode_config_init() to reflect the garbage-collected implementation?
I think that makes sense, at least as a wrapper with a __must_check annotation (and keeping the old one). I tried to avoid the rename because it'd be a flag day patch.
If you think this would be a great improvement then I can do that - I'm positive enough to bother with the rebase to touch up all the driver patches, but only if there's someone else who's enthusiastic about it :-)
When reading the patches, I thought that it's now non-obvious why some drivers cleanup drm_mode_config_init() manually and some don't. So you may not need a big flag-day patch. A simple wrapper with the drmm_ prefix should do. At some point the old interface will hopefully be gone.
Best regards Thomas
-Daniel
Best regards Thomas
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 2b1ba2ad5582..1e6291407586 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,6 +18,13 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+#define drmm_add_action_or_reset(dev, action, data) \
__drmm_add_action_or_reset(dev, action, data, #action)
+int __must_check __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data); diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 3bcbe30339f0..160a3e4b51c3 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -929,7 +929,7 @@ struct drm_mode_config { const struct drm_mode_config_helper_funcs *helper_private; };
-void drm_mode_config_init(struct drm_device *dev); +int drm_mode_config_init(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev);
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
Hi Daniel.
Some bikeshedding in the following. with or with addressing (IMHO valid points) consider the patch:
Reviewed-by: Sam Ravnborg sam@ravnborg.org
Sam
On Thu, Feb 27, 2020 at 07:14:57PM +0100, Daniel Vetter wrote:
drm_mode_config_cleanup is idempotent, so no harm in calling this twice. This allows us to gradually switch drivers over by removing explicit drm_mode_config_cleanup calls.
With this step it's not also possible that (at least for simple drivers) automatic resource cleanup can be done correctly without a drm_driver->release hook. Therefore allow this now in devm_drm_dev_init().
I am not really sure what you try to explain here? Should the "not" be deleted?
Also with drmm_ explicit drm_driver->release hooks are kinda not the best option, so deprecate that hook to discourage future users.
The ->release hooks has others uses until everything is moved over to drmm_, or so I think. So deprecation seems a lttle too soon.
v2: Fixup the example in the kerneldoc too.
v3:
- For paranoia, double check that minor->dev == dev in the release hook, because I botched the pointer math in the drmm library.
- Call drm_mode_config_cleanup when drmm_add_action fails, we'd be missing some mutex_destroy and ida_cleanup otherwise (Laurent)
v4: Add a drmm_add_action_or_reset (like devm_ has) to encapsulate this pattern (Noralf).
v5: Fix oversight in the new add_action_or_reset macro (Noralf)
^ drmm_add_action_or_reset
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Acked-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_drv.c | 23 +++++++---------------- drivers/gpu/drm/drm_managed.c | 14 ++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 13 ++++++++++++- include/drm/drm_managed.h | 7 +++++++ include/drm/drm_mode_config.h | 2 +- 5 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3cf40864d4a6..bb326b9bcde0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -98,6 +98,8 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) struct drm_minor *minor = data; unsigned long flags;
WARN_ON(dev != minor->dev);
put_device(minor->kdev);
spin_lock_irqsave(&drm_minor_lock, flags);
@@ -267,8 +269,7 @@ void drm_minor_release(struct drm_minor *minor)
- The following example shows a typical structure of a DRM display driver.
- The example focus on the probe() function and the other functions that is
- almost always present and serves as a demonstration of devm_drm_dev_init()
- usage with its accompanying drm_driver->release callback.
- almost always present and serves as a demonstration of devm_drm_dev_init().
- .. code-block:: c
@@ -278,16 +279,8 @@ void drm_minor_release(struct drm_minor *minor)
struct clk *pclk;
- };
- static void driver_drm_release(struct drm_device *drm)
- {
struct driver_device *priv = container_of(...);
drm_mode_config_cleanup(drm);
- }
- static struct drm_driver driver_drm_driver = {
[...]
.release = driver_drm_release,
- };
- static int driver_probe(struct platform_device *pdev)
@@ -312,7 +305,9 @@ void drm_minor_release(struct drm_minor *minor)
}
drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm);
ret = drm_mode_config_init(drm);
if (ret)
return ret;
We do not print anything in drm_mode_config_init() - so should we do it here? Otherwise we only get the more generic error from the driver core.
priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL);
if (!priv->userspace_facing)
@@ -710,8 +705,7 @@ static void devm_drm_dev_init_release(void *data)
- @driver: DRM driver
- Managed drm_dev_init(). The DRM device initialized with this function is
- automatically put on driver detach using drm_dev_put(). You must supply a
- &drm_driver.release callback to control the finalization explicitly.
- automatically put on driver detach using drm_dev_put().
- RETURNS:
- 0 on success, or error code on failure.
@@ -722,9 +716,6 @@ int devm_drm_dev_init(struct device *parent, { int ret;
- if (WARN_ON(!driver->release))
return -EINVAL;
- ret = drm_dev_init(dev, driver, parent); if (ret) return ret;
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 626656369f0b..6376be01bbc8 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -134,6 +134,20 @@ int __drmm_add_action(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action);
+int __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name)
+{
- int ret;
- ret = __drmm_add_action(dev, action, data, name);
- if (ret)
action(dev, data);
- return ret;
+} +EXPORT_SYMBOL(__drmm_add_action_or_reset);
Bikeshedding - but why oh why prefixing the function with two underscores? - It makes it less readable - It says "internal", at least to me - but it is exported - It makes the casual reader wonder why, removing focus from other more relevant things - It makes me writing several lines of rant
drmm_add_action_or_reset_named(...) would do the trick.
Same rant above goes for __drmm_add_action()...
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 08e6eff6a179..6f7005bc597f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -25,6 +25,7 @@ #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_file.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_config.h> #include <drm/drm_print.h> #include <linux/dma-resv.h> @@ -373,6 +374,11 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return 0; }
+static void drm_mode_config_init_release(struct drm_device *dev, void *ptr) +{
- drm_mode_config_cleanup(dev);
+}
/**
- drm_mode_config_init - initialize DRM mode_configuration structure
- @dev: DRM device
@@ -384,8 +390,10 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
- problem, since this should happen single threaded at init time. It is the
- driver's problem to ensure this guarantee.
- Cleanup is automatically handled through registering drm_mode_config_cleanup
*/
- with drmm_add_action().
-void drm_mode_config_init(struct drm_device *dev) +int drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); drm_modeset_lock_init(&dev->mode_config.connection_mutex); @@ -443,6 +451,9 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_acquire_fini(&modeset_ctx); dma_resv_fini(&resv); }
- return drmm_add_action_or_reset(dev, drm_mode_config_init_release,
NULL);
} EXPORT_SYMBOL(drm_mode_config_init);
As this is now a drmm_ managed function it should be named such. Maybe add a small drm_mode_config_init() wrapper in the header file for those that has not migrated yet. It is confusing if we are not consistent in naming and everywhere else the drm managed functions are named drmm_
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 2b1ba2ad5582..1e6291407586 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,6 +18,13 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+#define drmm_add_action_or_reset(dev, action, data) \
- __drmm_add_action_or_reset(dev, action, data, #action)
+int __must_check __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data); diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 3bcbe30339f0..160a3e4b51c3 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -929,7 +929,7 @@ struct drm_mode_config { const struct drm_mode_config_helper_funcs *helper_private; };
-void drm_mode_config_init(struct drm_device *dev); +int drm_mode_config_init(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev);
-- 2.24.1
On Fri, Feb 28, 2020 at 9:26 PM Sam Ravnborg sam@ravnborg.org wrote:
Hi Daniel.
Some bikeshedding in the following. with or with addressing (IMHO valid points) consider the patch:
Reviewed-by: Sam Ravnborg sam@ravnborg.org
Sam
On Thu, Feb 27, 2020 at 07:14:57PM +0100, Daniel Vetter wrote:
drm_mode_config_cleanup is idempotent, so no harm in calling this twice. This allows us to gradually switch drivers over by removing explicit drm_mode_config_cleanup calls.
With this step it's not also possible that (at least for simple drivers) automatic resource cleanup can be done correctly without a drm_driver->release hook. Therefore allow this now in devm_drm_dev_init().
I am not really sure what you try to explain here? Should the "not" be deleted?
s/not/now/
somehow that's a typo I do all the time, dunno why.
Also with drmm_ explicit drm_driver->release hooks are kinda not the best option, so deprecate that hook to discourage future users.
The ->release hooks has others uses until everything is moved over to drmm_, or so I think. So deprecation seems a lttle too soon.
You can just add a drmm action which calls your release function. The upshot is that you can be more fine-grained (useful for unwinding when driver load fails halfway through). That's why I think new drivers after this patch shouldn't use ->release anymore, it's strictly less useful than drmm actions. The less unwind code I have to review carefully to make sure the right stuff gets released (and not more!) the better.
v2: Fixup the example in the kerneldoc too.
v3:
- For paranoia, double check that minor->dev == dev in the release hook, because I botched the pointer math in the drmm library.
- Call drm_mode_config_cleanup when drmm_add_action fails, we'd be missing some mutex_destroy and ida_cleanup otherwise (Laurent)
v4: Add a drmm_add_action_or_reset (like devm_ has) to encapsulate this pattern (Noralf).
v5: Fix oversight in the new add_action_or_reset macro (Noralf)
^ drmm_add_action_or_reset
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Acked-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_drv.c | 23 +++++++---------------- drivers/gpu/drm/drm_managed.c | 14 ++++++++++++++ drivers/gpu/drm/drm_mode_config.c | 13 ++++++++++++- include/drm/drm_managed.h | 7 +++++++ include/drm/drm_mode_config.h | 2 +- 5 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3cf40864d4a6..bb326b9bcde0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -98,6 +98,8 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data) struct drm_minor *minor = data; unsigned long flags;
WARN_ON(dev != minor->dev);
put_device(minor->kdev); spin_lock_irqsave(&drm_minor_lock, flags);
@@ -267,8 +269,7 @@ void drm_minor_release(struct drm_minor *minor)
- The following example shows a typical structure of a DRM display driver.
- The example focus on the probe() function and the other functions that is
- almost always present and serves as a demonstration of devm_drm_dev_init()
- usage with its accompanying drm_driver->release callback.
- almost always present and serves as a demonstration of devm_drm_dev_init().
- .. code-block:: c
@@ -278,16 +279,8 @@ void drm_minor_release(struct drm_minor *minor)
struct clk *pclk;
- };
- static void driver_drm_release(struct drm_device *drm)
- {
struct driver_device *priv = container_of(...);
drm_mode_config_cleanup(drm);
- }
- static struct drm_driver driver_drm_driver = {
[...]
.release = driver_drm_release,
- };
- static int driver_probe(struct platform_device *pdev)
@@ -312,7 +305,9 @@ void drm_minor_release(struct drm_minor *minor)
}
drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm);
ret = drm_mode_config_init(drm);
if (ret)
return ret;
We do not print anything in drm_mode_config_init() - so should we do it here? Otherwise we only get the more generic error from the driver core.
I can add a printk to drm_mode_config if people feel like. But it's guaranteed dead code in reality, because of linux' small memory allocation guarantee. Small mallocs like this one here of just 2 cachelines never fail (at least not with GFP_KERNEL).
priv->userspace_facing = drmm_kzalloc(..., GFP_KERNEL);
if (!priv->userspace_facing)
@@ -710,8 +705,7 @@ static void devm_drm_dev_init_release(void *data)
- @driver: DRM driver
- Managed drm_dev_init(). The DRM device initialized with this function is
- automatically put on driver detach using drm_dev_put(). You must supply a
- &drm_driver.release callback to control the finalization explicitly.
- automatically put on driver detach using drm_dev_put().
- RETURNS:
- 0 on success, or error code on failure.
@@ -722,9 +716,6 @@ int devm_drm_dev_init(struct device *parent, { int ret;
if (WARN_ON(!driver->release))
return -EINVAL;
ret = drm_dev_init(dev, driver, parent); if (ret) return ret;
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 626656369f0b..6376be01bbc8 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -134,6 +134,20 @@ int __drmm_add_action(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action);
+int __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name)
+{
int ret;
ret = __drmm_add_action(dev, action, data, name);
if (ret)
action(dev, data);
return ret;
+} +EXPORT_SYMBOL(__drmm_add_action_or_reset);
Bikeshedding - but why oh why prefixing the function with two underscores?
- It makes it less readable
- It says "internal", at least to me - but it is exported
- It makes the casual reader wonder why, removing focus from other more relevant things
- It makes me writing several lines of rant
drmm_add_action_or_reset_named(...) would do the trick.
It _is_ an internal function. I don't think drivers should ever call it. Hence __ and hence no kerneldoc in the last patch. Now if someone finds a legit use for this I guess we cold rename it, and give it some nice kerneldoc. But imo no point before that.
A thing that might be useful is drm_add_action_f() with a format string at the end, for cases where you set a non-NULL data pointer, and want to include that somehow in the name. This might be useful for cleanup actions for stuff like connectors and so on. But we're not there yet at all, and you can also do that kind of meaningful debug output from cleanup function directly.
Same rant above goes for __drmm_add_action()...
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 08e6eff6a179..6f7005bc597f 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -25,6 +25,7 @@ #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_file.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_config.h> #include <drm/drm_print.h> #include <linux/dma-resv.h> @@ -373,6 +374,11 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return 0; }
+static void drm_mode_config_init_release(struct drm_device *dev, void *ptr) +{
drm_mode_config_cleanup(dev);
+}
/**
- drm_mode_config_init - initialize DRM mode_configuration structure
- @dev: DRM device
@@ -384,8 +390,10 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
- problem, since this should happen single threaded at init time. It is the
- driver's problem to ensure this guarantee.
- Cleanup is automatically handled through registering drm_mode_config_cleanup
*/
- with drmm_add_action().
-void drm_mode_config_init(struct drm_device *dev) +int drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); drm_modeset_lock_init(&dev->mode_config.connection_mutex); @@ -443,6 +451,9 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_acquire_fini(&modeset_ctx); dma_resv_fini(&resv); }
return drmm_add_action_or_reset(dev, drm_mode_config_init_release,
NULL);
} EXPORT_SYMBOL(drm_mode_config_init);
As this is now a drmm_ managed function it should be named such. Maybe add a small drm_mode_config_init() wrapper in the header file for those that has not migrated yet. It is confusing if we are not consistent in naming and everywhere else the drm managed functions are named drmm_
Yeah ok I guess I'm convinced on this :-)
Cheers, Daniel
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 2b1ba2ad5582..1e6291407586 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,6 +18,13 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+#define drmm_add_action_or_reset(dev, action, data) \
__drmm_add_action_or_reset(dev, action, data, #action)
+int __must_check __drmm_add_action_or_reset(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data); diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 3bcbe30339f0..160a3e4b51c3 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -929,7 +929,7 @@ struct drm_mode_config { const struct drm_mode_config_helper_funcs *helper_private; };
-void drm_mode_config_init(struct drm_device *dev); +int drm_mode_config_init(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev);
-- 2.24.1
Hi Daniel.
Also with drmm_ explicit drm_driver->release hooks are kinda not the best option, so deprecate that hook to discourage future users.
The ->release hooks has others uses until everything is moved over to drmm_, or so I think. So deprecation seems a lttle too soon.
You can just add a drmm action which calls your release function. The upshot is that you can be more fine-grained (useful for unwinding when driver load fails halfway through). That's why I think new drivers after this patch shouldn't use ->release anymore, it's strictly less useful than drmm actions. The less unwind code I have to review carefully to make sure the right stuff gets released (and not more!) the better. From that perspective I agree - gently pushing people to use drmm_
is only good.
Sam
On Sat, Feb 29, 2020 at 12:11:28AM +0100, Daniel Vetter wrote:
On Fri, Feb 28, 2020 at 9:26 PM Sam Ravnborg sam@ravnborg.org wrote:
@@ -312,7 +305,9 @@ void drm_minor_release(struct drm_minor *minor)
}
drmm_add_final_kfree(drm, priv);
drm_mode_config_init(drm);
ret = drm_mode_config_init(drm);
if (ret)
return ret;
We do not print anything in drm_mode_config_init() - so should we do it here? Otherwise we only get the more generic error from the driver core.
I can add a printk to drm_mode_config if people feel like. But it's guaranteed dead code in reality, because of linux' small memory allocation guarantee. Small mallocs like this one here of just 2 cachelines never fail (at least not with GFP_KERNEL).
To make this not quite pointless I decided to add debug output to drmm_add_action and drm_kmalloc. I think there it's actually useful for debugging. Will squash that into other patches. -Daniel
Small mistake that crept into
commit 81da8c3b8d3df6f05b11300b7d17ccd1f3017fab Author: Gerd Hoffmann kraxel@redhat.com Date: Tue Feb 11 14:52:18 2020 +0100
drm/bochs: add drm_driver.release callback
where drm_atomic_helper_shutdown was left in both places. The ->release callback really shouldn't touch hardware.
Cc: Gerd Hoffmann kraxel@redhat.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/bochs/bochs_kms.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index 8066d7d370d5..e8cc8156d773 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -166,6 +166,5 @@ void bochs_kms_fini(struct bochs_device *bochs) if (!bochs->dev->mode_config.num_connector) return;
- drm_atomic_helper_shutdown(bochs->dev); drm_mode_config_cleanup(bochs->dev); }
Instead rely on the automatic clean, for which we just need to check that drm_mode_config_init succeeded. To avoid an inversion in the cleanup we also have to move the dev_private allocation over to drmm_kzalloc.
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: virtualization@lists.linux-foundation.org --- drivers/gpu/drm/bochs/bochs.h | 1 - drivers/gpu/drm/bochs/bochs_drv.c | 6 ++---- drivers/gpu/drm/bochs/bochs_kms.c | 14 +++++--------- 3 files changed, 7 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h index 917767173ee6..e5bd1d517a18 100644 --- a/drivers/gpu/drm/bochs/bochs.h +++ b/drivers/gpu/drm/bochs/bochs.h @@ -92,7 +92,6 @@ void bochs_mm_fini(struct bochs_device *bochs);
/* bochs_kms.c */ int bochs_kms_init(struct bochs_device *bochs); -void bochs_kms_fini(struct bochs_device *bochs);
/* bochs_fbdev.c */ extern const struct drm_mode_config_funcs bochs_mode_funcs; diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index addb0568c1af..e18c51de1196 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -7,6 +7,7 @@
#include <drm/drm_drv.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_managed.h>
#include "bochs.h"
@@ -21,10 +22,7 @@ static void bochs_unload(struct drm_device *dev) { struct bochs_device *bochs = dev->dev_private;
- bochs_kms_fini(bochs); bochs_mm_fini(bochs); - kfree(bochs); - dev->dev_private = NULL; }
static int bochs_load(struct drm_device *dev) @@ -32,7 +30,7 @@ static int bochs_load(struct drm_device *dev) struct bochs_device *bochs; int ret;
- bochs = kzalloc(sizeof(*bochs), GFP_KERNEL); + bochs = drmm_kzalloc(dev, sizeof(*bochs), GFP_KERNEL); if (bochs == NULL) return -ENOMEM; dev->dev_private = bochs; diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index e8cc8156d773..8285c03a6a95 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -134,7 +134,11 @@ const struct drm_mode_config_funcs bochs_mode_funcs = {
int bochs_kms_init(struct bochs_device *bochs) { - drm_mode_config_init(bochs->dev); + int ret; + + ret = drm_mode_config_init(bochs->dev); + if (ret) + return ret;
bochs->dev->mode_config.max_width = 8192; bochs->dev->mode_config.max_height = 8192; @@ -160,11 +164,3 @@ int bochs_kms_init(struct bochs_device *bochs)
return 0; } - -void bochs_kms_fini(struct bochs_device *bochs) -{ - if (!bochs->dev->mode_config.num_connector) - return; - - drm_mode_config_cleanup(bochs->dev); -}
We can even delete the drm_driver.release hook now!
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: virtualization@lists.linux-foundation.org --- drivers/gpu/drm/cirrus/cirrus.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index a9d789a56536..6ac0286810ec 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c @@ -510,11 +510,15 @@ static const struct drm_mode_config_funcs cirrus_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, };
-static void cirrus_mode_config_init(struct cirrus_device *cirrus) +static int cirrus_mode_config_init(struct cirrus_device *cirrus) { struct drm_device *dev = &cirrus->dev; + int ret; + + ret = drm_mode_config_init(dev); + if (ret) + return ret;
- drm_mode_config_init(dev); dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2; @@ -522,15 +526,12 @@ static void cirrus_mode_config_init(struct cirrus_device *cirrus) dev->mode_config.preferred_depth = 16; dev->mode_config.prefer_shadow = 0; dev->mode_config.funcs = &cirrus_mode_config_funcs; + + return 0; }
/* ------------------------------------------------------------------ */
-static void cirrus_release(struct drm_device *dev) -{ - drm_mode_config_cleanup(dev); -} - DEFINE_DRM_GEM_FOPS(cirrus_fops);
static struct drm_driver cirrus_driver = { @@ -544,7 +545,6 @@ static struct drm_driver cirrus_driver = {
.fops = &cirrus_fops, DRM_GEM_SHMEM_DRIVER_OPS, - .release = cirrus_release, };
static int cirrus_pci_probe(struct pci_dev *pdev, @@ -591,7 +591,9 @@ static int cirrus_pci_probe(struct pci_dev *pdev, if (cirrus->mmio == NULL) goto err_unmap_vram;
- cirrus_mode_config_init(cirrus); + ret = cirrus_mode_config_init(cirrus); + if (ret) + goto err_cleanup;
ret = cirrus_conn_init(cirrus); if (ret < 0) @@ -613,7 +615,6 @@ static int cirrus_pci_probe(struct pci_dev *pdev, return 0;
err_cleanup: - drm_mode_config_cleanup(dev); iounmap(cirrus->mmio); err_unmap_vram: iounmap(cirrus->vram);
On Thu, Feb 27, 2020 at 07:15:00PM +0100, Daniel Vetter wrote:
We can even delete the drm_driver.release hook now!
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: virtualization@lists.linux-foundation.org
Acked-by: Sam Ravnborg sam@ravnborg.org
But as stated in other post - using drmm_mode_config_init() would make this more readable.
Sam
drivers/gpu/drm/cirrus/cirrus.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index a9d789a56536..6ac0286810ec 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c @@ -510,11 +510,15 @@ static const struct drm_mode_config_funcs cirrus_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, };
-static void cirrus_mode_config_init(struct cirrus_device *cirrus) +static int cirrus_mode_config_init(struct cirrus_device *cirrus) { struct drm_device *dev = &cirrus->dev;
- int ret;
- ret = drm_mode_config_init(dev);
- if (ret)
return ret;
- drm_mode_config_init(dev); dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2;
@@ -522,15 +526,12 @@ static void cirrus_mode_config_init(struct cirrus_device *cirrus) dev->mode_config.preferred_depth = 16; dev->mode_config.prefer_shadow = 0; dev->mode_config.funcs = &cirrus_mode_config_funcs;
- return 0;
}
/* ------------------------------------------------------------------ */
-static void cirrus_release(struct drm_device *dev) -{
- drm_mode_config_cleanup(dev);
-}
DEFINE_DRM_GEM_FOPS(cirrus_fops);
static struct drm_driver cirrus_driver = { @@ -544,7 +545,6 @@ static struct drm_driver cirrus_driver = {
.fops = &cirrus_fops, DRM_GEM_SHMEM_DRIVER_OPS,
- .release = cirrus_release,
};
static int cirrus_pci_probe(struct pci_dev *pdev, @@ -591,7 +591,9 @@ static int cirrus_pci_probe(struct pci_dev *pdev, if (cirrus->mmio == NULL) goto err_unmap_vram;
- cirrus_mode_config_init(cirrus);
ret = cirrus_mode_config_init(cirrus);
if (ret)
goto err_cleanup;
ret = cirrus_conn_init(cirrus); if (ret < 0)
@@ -613,7 +615,6 @@ static int cirrus_pci_probe(struct pci_dev *pdev, return 0;
err_cleanup:
- drm_mode_config_cleanup(dev); iounmap(cirrus->mmio);
err_unmap_vram: iounmap(cirrus->vram); -- 2.24.1
With the drm_device lifetime fun cleaned up there's nothing in the way anymore to use devm_ for everything hw releated. Do it, and in the process, throw out the entire onion unwinding.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Emil Velikov emil.velikov@collabora.com Cc: Thomas Zimmermann tzimmermann@suse.de Cc: virtualization@lists.linux-foundation.org --- drivers/gpu/drm/cirrus/cirrus.c | 44 +++++++++++---------------------- 1 file changed, 14 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index 6ac0286810ec..1b78a2f88f69 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c @@ -558,7 +558,7 @@ static int cirrus_pci_probe(struct pci_dev *pdev, if (ret) return ret;
- ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) return ret;
@@ -569,39 +569,38 @@ static int cirrus_pci_probe(struct pci_dev *pdev, ret = -ENOMEM; cirrus = kzalloc(sizeof(*cirrus), GFP_KERNEL); if (cirrus == NULL) - goto err_pci_release; + return ret;
dev = &cirrus->dev; - ret = drm_dev_init(dev, &cirrus_driver, &pdev->dev); + ret = devm_drm_dev_init(&pdev->dev, dev, &cirrus_driver); if (ret) { kfree(cirrus); - goto err_pci_release; + return ret; } dev->dev_private = cirrus; drmm_add_final_kfree(dev, cirrus);
- ret = -ENOMEM; - cirrus->vram = ioremap(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); + cirrus->vram = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); if (cirrus->vram == NULL) - goto err_dev_put; + return -ENOMEM;
- cirrus->mmio = ioremap(pci_resource_start(pdev, 1), - pci_resource_len(pdev, 1)); + cirrus->mmio = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 1), + pci_resource_len(pdev, 1)); if (cirrus->mmio == NULL) - goto err_unmap_vram; + return -ENOMEM;
ret = cirrus_mode_config_init(cirrus); if (ret) - goto err_cleanup; + return ret;
ret = cirrus_conn_init(cirrus); if (ret < 0) - goto err_cleanup; + return ret;
ret = cirrus_pipe_init(cirrus); if (ret < 0) - goto err_cleanup; + return ret;
drm_mode_config_reset(dev);
@@ -609,33 +608,18 @@ static int cirrus_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, dev); ret = drm_dev_register(dev, 0); if (ret) - goto err_cleanup; + return ret;
drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth); return 0; - -err_cleanup: - iounmap(cirrus->mmio); -err_unmap_vram: - iounmap(cirrus->vram); -err_dev_put: - drm_dev_put(dev); -err_pci_release: - pci_release_regions(pdev); - return ret; }
static void cirrus_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - struct cirrus_device *cirrus = dev->dev_private;
drm_dev_unplug(dev); drm_atomic_helper_shutdown(dev); - iounmap(cirrus->mmio); - iounmap(cirrus->vram); - drm_dev_put(dev); - pci_release_regions(pdev); }
static const struct pci_device_id pciidlist[] = {
Allows us to drop the drm_driver.release callback.
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Paul Cercueil paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index 192aaa4421a3..f5689521428e 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -489,11 +489,6 @@ static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) return IRQ_HANDLED; }
-static void ingenic_drm_release(struct drm_device *drm) -{ - drm_mode_config_cleanup(drm); -} - static int ingenic_drm_enable_vblank(struct drm_crtc *crtc) { struct ingenic_drm *priv = drm_crtc_get_priv(crtc); @@ -537,7 +532,6 @@ static struct drm_driver ingenic_drm_driver_data = { .gem_prime_mmap = drm_gem_cma_prime_mmap,
.irq_handler = ingenic_drm_irq_handler, - .release = ingenic_drm_release, };
static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = { @@ -638,7 +632,10 @@ static int ingenic_drm_probe(struct platform_device *pdev) } drmm_add_final_kfree(drm, priv);
- drm_mode_config_init(drm); + ret = drm_mode_config_init(drm); + if (ret) + return ret; + drm->mode_config.min_width = 0; drm->mode_config.min_height = 0; drm->mode_config.max_width = soc_info->max_width;
Hi Daniel,
Le jeu., févr. 27, 2020 at 19:15, Daniel Vetter daniel.vetter@ffwll.ch a écrit :
Allows us to drop the drm_driver.release callback.
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Paul Cercueil paul@crapouillou.net
Reviewed-by: Paul Cercueil paul@crapouillou.net
Cheers, -Paul
drivers/gpu/drm/ingenic/ingenic-drm.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index 192aaa4421a3..f5689521428e 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -489,11 +489,6 @@ static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) return IRQ_HANDLED; }
-static void ingenic_drm_release(struct drm_device *drm) -{
- drm_mode_config_cleanup(drm);
-}
static int ingenic_drm_enable_vblank(struct drm_crtc *crtc) { struct ingenic_drm *priv = drm_crtc_get_priv(crtc); @@ -537,7 +532,6 @@ static struct drm_driver ingenic_drm_driver_data = { .gem_prime_mmap = drm_gem_cma_prime_mmap,
.irq_handler = ingenic_drm_irq_handler,
- .release = ingenic_drm_release,
};
static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = { @@ -638,7 +632,10 @@ static int ingenic_drm_probe(struct platform_device *pdev) } drmm_add_final_kfree(drm, priv);
- drm_mode_config_init(drm);
- ret = drm_mode_config_init(drm);
- if (ret)
return ret;
- drm->mode_config.min_width = 0; drm->mode_config.min_height = 0; drm->mode_config.max_width = soc_info->max_width;
-- 2.24.1
Allows us to drop the drm_driver.release callback.
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Reviewed-by: Linus Walleij linus.walleij@linaro.org Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Linus Walleij linus.walleij@linaro.org --- drivers/gpu/drm/mcde/mcde_drv.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index a543ebf3d541..b8ca99995e51 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -184,13 +184,13 @@ static int mcde_modeset_init(struct drm_device *drm) ret = drm_vblank_init(drm, 1); if (ret) { dev_err(drm->dev, "failed to init vblank\n"); - goto out_config; + return ret; }
ret = mcde_display_init(drm); if (ret) { dev_err(drm->dev, "failed to init display\n"); - goto out_config; + return ret; }
/* @@ -204,7 +204,7 @@ static int mcde_modeset_init(struct drm_device *drm) mcde->bridge); if (ret) { dev_err(drm->dev, "failed to attach display output bridge\n"); - goto out_config; + return ret; }
drm_mode_config_reset(drm); @@ -212,17 +212,6 @@ static int mcde_modeset_init(struct drm_device *drm) drm_fbdev_generic_setup(drm, 32);
return 0; - -out_config: - drm_mode_config_cleanup(drm); - return ret; -} - -static void mcde_release(struct drm_device *drm) -{ - struct mcde *mcde = drm->dev_private; - - drm_mode_config_cleanup(drm); }
DEFINE_DRM_GEM_CMA_FOPS(drm_fops); @@ -230,7 +219,6 @@ DEFINE_DRM_GEM_CMA_FOPS(drm_fops); static struct drm_driver mcde_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, - .release = mcde_release, .lastclose = drm_fb_helper_lastclose, .ioctls = NULL, .fops = &drm_fops, @@ -258,7 +246,9 @@ static int mcde_drm_bind(struct device *dev) struct drm_device *drm = dev_get_drvdata(dev); int ret;
- drm_mode_config_init(drm); + ret = drm_mode_config_init(drm); + if (ret) + return ret;
ret = component_bind_all(drm->dev, drm); if (ret) {
Auto-unwind ftw, now possible with the fixed drm_device related management.
Aside, clk/regulator seem to be missing devm versions for a bunch of functions, preventing a pile of these simpler drivers from outright losing their ->remove hook.
Reviewed-by: Linus Walleij linus.walleij@linaro.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Linus Walleij linus.walleij@linaro.org --- drivers/gpu/drm/mcde/mcde_drv.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index b8ca99995e51..3e92a44397cf 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -312,7 +312,7 @@ static int mcde_probe(struct platform_device *pdev) return -ENOMEM; mcde->dev = dev;
- ret = drm_dev_init(&mcde->drm, &mcde_drm_driver, dev); + ret = devm_drm_dev_init(dev, &mcde->drm, &mcde_drm_driver); if (ret) { kfree(mcde); return ret; @@ -331,12 +331,12 @@ static int mcde_probe(struct platform_device *pdev) if (IS_ERR(mcde->epod)) { ret = PTR_ERR(mcde->epod); dev_err(dev, "can't get EPOD regulator\n"); - goto dev_unref; + return ret; } ret = regulator_enable(mcde->epod); if (ret) { dev_err(dev, "can't enable EPOD regulator\n"); - goto dev_unref; + return ret; } mcde->vana = devm_regulator_get(dev, "vana"); if (IS_ERR(mcde->vana)) { @@ -487,8 +487,6 @@ static int mcde_probe(struct platform_device *pdev) regulator_disable(mcde->vana); regulator_epod_off: regulator_disable(mcde->epod); -dev_unref: - drm_dev_put(drm); return ret;
} @@ -502,7 +500,6 @@ static int mcde_remove(struct platform_device *pdev) clk_disable_unprepare(mcde->mcde_clk); regulator_disable(mcde->vana); regulator_disable(mcde->epod); - drm_dev_put(drm);
return 0; }
It's right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: This driver gets its devm_ stuff all wrong wrt drm_device and anything hanging off that. Not the only one unfortunately.
v2: Explain why this cleanup is possible (Laurent).
Reviewed-by: Neil Armstrong narmstrong@baylibre.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Neil Armstrong narmstrong@baylibre.com Cc: Kevin Hilman khilman@baylibre.com Cc: linux-amlogic@lists.infradead.org Cc: linux-arm-kernel@lists.infradead.org --- drivers/gpu/drm/meson/meson_drv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index b5f5eb7b4bb9..ae94d14ab7bc 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -284,7 +284,9 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) /* Remove early framebuffers (ie. simplefb) */ meson_remove_framebuffers();
- drm_mode_config_init(drm); + ret = drm_mode_config_init(drm); + if (ret) + goto free_drm; drm->mode_config.max_width = 3840; drm->mode_config.max_height = 2160; drm->mode_config.funcs = &meson_mode_config_funcs; @@ -379,7 +381,6 @@ static void meson_drv_unbind(struct device *dev) drm_dev_unregister(drm); drm_irq_uninstall(drm); drm_kms_helper_poll_fini(drm); - drm_mode_config_cleanup(drm); drm_dev_put(drm); }
It's right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: This driver gets its devm_ stuff all wrong wrt drm_device and anything hanging off that. Not the only one unfortunately.
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Eric Anholt eric@anholt.net --- drivers/gpu/drm/pl111/pl111_drv.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index aa8aa8d9e405..87b9b32c90a8 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -90,10 +90,13 @@ static int pl111_modeset_init(struct drm_device *dev) struct drm_panel *panel = NULL; struct drm_bridge *bridge = NULL; bool defer = false; - int ret = 0; + int ret; int i;
- drm_mode_config_init(dev); + ret = drm_mode_config_init(dev); + if (ret) + return ret; + mode_config = &dev->mode_config; mode_config->funcs = &mode_config_funcs; mode_config->min_width = 1; @@ -154,7 +157,7 @@ static int pl111_modeset_init(struct drm_device *dev) DRM_MODE_CONNECTOR_Unknown); if (IS_ERR(bridge)) { ret = PTR_ERR(bridge); - goto out_config; + goto finish; } } else if (bridge) { dev_info(dev->dev, "Using non-panel bridge\n"); @@ -197,8 +200,6 @@ static int pl111_modeset_init(struct drm_device *dev) out_bridge: if (panel) drm_panel_bridge_remove(bridge); -out_config: - drm_mode_config_cleanup(dev); finish: return ret; } @@ -343,7 +344,6 @@ static int pl111_amba_remove(struct amba_device *amba_dev) drm_dev_unregister(drm); if (priv->panel) drm_panel_bridge_remove(priv->bridge); - drm_mode_config_cleanup(drm); drm_dev_put(drm); of_reserved_mem_device_release(dev);
It's right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: Another driver with a bit much devm_kzalloc, which should probably use drmm_kzalloc instead ...
v2: Explain why this cleanup is possible (Laurent).
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Kieran Bingham kieran.bingham+renesas@ideasonboard.com Cc: linux-renesas-soc@vger.kernel.org --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 1 - drivers/gpu/drm/rcar-du/rcar_du_kms.c | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 654e2dd08146..3e67cf70f040 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -530,7 +530,6 @@ static int rcar_du_remove(struct platform_device *pdev) drm_dev_unregister(ddev);
drm_kms_helper_poll_fini(ddev); - drm_mode_config_cleanup(ddev);
drm_dev_put(ddev);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index fcfd916227d1..dcdc1580b511 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -712,7 +712,9 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) unsigned int i; int ret;
- drm_mode_config_init(dev); + ret = drm_mode_config_init(dev); + if (ret) + return ret;
dev->mode_config.min_width = 0; dev->mode_config.min_height = 0;
It's (almost, there's some iommu stuff without significance) right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: Another driver with a bit much devm_kzalloc, which should probably use drmm_kzalloc instead ...
v2: Explain why this cleanup is possible (Laurent).
v3: Jump out at the right label (Francesco)
Cc: Francesco Lavra francescolavra.fl@gmail.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Sandy Huang hjc@rock-chips.com Cc: "Heiko Stübner" heiko@sntech.de Cc: linux-arm-kernel@lists.infradead.org Cc: linux-rockchip@lists.infradead.org --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 20ecb1508a22..9b2502f92018 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -135,7 +135,9 @@ static int rockchip_drm_bind(struct device *dev) if (ret) goto err_free;
- drm_mode_config_init(drm_dev); + ret = drm_mode_config_init(drm_dev); + if (ret) + goto err_iommu_cleanup;
rockchip_drm_mode_config_init(drm_dev);
@@ -173,12 +175,9 @@ static int rockchip_drm_bind(struct device *dev) rockchip_drm_fbdev_fini(drm_dev); err_unbind_all: component_unbind_all(dev, drm_dev); -err_mode_config_cleanup: - drm_mode_config_cleanup(drm_dev); +err_iommu_cleanup: rockchip_iommu_cleanup(drm_dev); err_free: - drm_dev->dev_private = NULL; - dev_set_drvdata(dev, NULL); drm_dev_put(drm_dev); return ret; } @@ -194,11 +193,8 @@ static void rockchip_drm_unbind(struct device *dev)
drm_atomic_helper_shutdown(drm_dev); component_unbind_all(dev, drm_dev); - drm_mode_config_cleanup(drm_dev); rockchip_iommu_cleanup(drm_dev);
- drm_dev->dev_private = NULL; - dev_set_drvdata(dev, NULL); drm_dev_put(drm_dev); }
Hi Daniel,
I love your patch! Yet something to improve:
[auto build test ERROR on drm-tip/drm-tip] [also build test ERROR on next-20200228] [cannot apply to drm-intel/for-linux-next linus/master pinchartl-media/drm/du/next v5.6-rc3] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Daniel-Vetter/drm-managed-resources... base: git://anongit.freedesktop.org/drm/drm-tip drm-tip config: arm64-defconfig (attached as .config) compiler: aarch64-linux-gcc (GCC) 7.5.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.5.0 make.cross ARCH=arm64
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
drivers/gpu/drm/rockchip/rockchip_drm_drv.c: In function 'rockchip_drm_bind':
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:147:3: error: label 'err_mode_config_cleanup' used but not defined
goto err_mode_config_cleanup; ^~~~
vim +/err_mode_config_cleanup +147 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
2048e3286f347db Mark Yao 2014-08-22 110 f706974a69b6e2b Tomeu Vizoso 2016-06-10 111 static int rockchip_drm_bind(struct device *dev) 2048e3286f347db Mark Yao 2014-08-22 112 { f706974a69b6e2b Tomeu Vizoso 2016-06-10 113 struct drm_device *drm_dev; 2048e3286f347db Mark Yao 2014-08-22 114 struct rockchip_drm_private *private; 2048e3286f347db Mark Yao 2014-08-22 115 int ret; 2048e3286f347db Mark Yao 2014-08-22 116 f706974a69b6e2b Tomeu Vizoso 2016-06-10 117 drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); 0f2886057be322d Tom Gundersen 2016-09-21 118 if (IS_ERR(drm_dev)) 0f2886057be322d Tom Gundersen 2016-09-21 119 return PTR_ERR(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 120 f706974a69b6e2b Tomeu Vizoso 2016-06-10 121 dev_set_drvdata(dev, drm_dev); f706974a69b6e2b Tomeu Vizoso 2016-06-10 122 f706974a69b6e2b Tomeu Vizoso 2016-06-10 123 private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); f706974a69b6e2b Tomeu Vizoso 2016-06-10 124 if (!private) { f706974a69b6e2b Tomeu Vizoso 2016-06-10 125 ret = -ENOMEM; 9127f99c4801f32 Tomasz Figa 2016-06-21 126 goto err_free; f706974a69b6e2b Tomeu Vizoso 2016-06-10 127 } f706974a69b6e2b Tomeu Vizoso 2016-06-10 128 2048e3286f347db Mark Yao 2014-08-22 129 drm_dev->dev_private = private; 2048e3286f347db Mark Yao 2014-08-22 130 5182c1a556d7ff7 Yakir Yang 2016-07-24 131 INIT_LIST_HEAD(&private->psr_list); 60beeccc72cabef Sean Paul 2018-03-05 132 mutex_init(&private->psr_list_lock); 5182c1a556d7ff7 Yakir Yang 2016-07-24 133 ccea91998c8f140 Jeffy Chen 2017-04-06 134 ret = rockchip_drm_init_iommu(drm_dev); ccea91998c8f140 Jeffy Chen 2017-04-06 135 if (ret) ccea91998c8f140 Jeffy Chen 2017-04-06 136 goto err_free; ccea91998c8f140 Jeffy Chen 2017-04-06 137 7db42e97bb41bd5 Daniel Vetter 2020-02-27 138 ret = drm_mode_config_init(drm_dev); 7db42e97bb41bd5 Daniel Vetter 2020-02-27 139 if (ret) 7db42e97bb41bd5 Daniel Vetter 2020-02-27 140 goto err_iommu_cleanup; 2048e3286f347db Mark Yao 2014-08-22 141 2048e3286f347db Mark Yao 2014-08-22 142 rockchip_drm_mode_config_init(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 143 2048e3286f347db Mark Yao 2014-08-22 144 /* Try to bind all sub drivers. */ 2048e3286f347db Mark Yao 2014-08-22 145 ret = component_bind_all(dev, drm_dev); 2048e3286f347db Mark Yao 2014-08-22 146 if (ret) ccea91998c8f140 Jeffy Chen 2017-04-06 @147 goto err_mode_config_cleanup; 2048e3286f347db Mark Yao 2014-08-22 148 ccea91998c8f140 Jeffy Chen 2017-04-06 149 ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); ccea91998c8f140 Jeffy Chen 2017-04-06 150 if (ret) ccea91998c8f140 Jeffy Chen 2017-04-06 151 goto err_unbind_all; ccea91998c8f140 Jeffy Chen 2017-04-06 152 ccea91998c8f140 Jeffy Chen 2017-04-06 153 drm_mode_config_reset(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 154 2048e3286f347db Mark Yao 2014-08-22 155 /* 2048e3286f347db Mark Yao 2014-08-22 156 * enable drm irq mode. 2048e3286f347db Mark Yao 2014-08-22 157 * - with irq_enabled = true, we can use the vblank feature. 2048e3286f347db Mark Yao 2014-08-22 158 */ 2048e3286f347db Mark Yao 2014-08-22 159 drm_dev->irq_enabled = true; 2048e3286f347db Mark Yao 2014-08-22 160 2048e3286f347db Mark Yao 2014-08-22 161 ret = rockchip_drm_fbdev_init(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 162 if (ret) 8415ab565da966b Mark Yao 2017-08-01 163 goto err_unbind_all; 8415ab565da966b Mark Yao 2017-08-01 164 8415ab565da966b Mark Yao 2017-08-01 165 /* init kms poll for handling hpd */ 8415ab565da966b Mark Yao 2017-08-01 166 drm_kms_helper_poll_init(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 167 9127f99c4801f32 Tomasz Figa 2016-06-21 168 ret = drm_dev_register(drm_dev, 0); 9127f99c4801f32 Tomasz Figa 2016-06-21 169 if (ret) 8415ab565da966b Mark Yao 2017-08-01 170 goto err_kms_helper_poll_fini; 9127f99c4801f32 Tomasz Figa 2016-06-21 171 2048e3286f347db Mark Yao 2014-08-22 172 return 0; 2048e3286f347db Mark Yao 2014-08-22 173 err_kms_helper_poll_fini: 2048e3286f347db Mark Yao 2014-08-22 174 drm_kms_helper_poll_fini(drm_dev); 8415ab565da966b Mark Yao 2017-08-01 175 rockchip_drm_fbdev_fini(drm_dev); ccea91998c8f140 Jeffy Chen 2017-04-06 176 err_unbind_all: 2048e3286f347db Mark Yao 2014-08-22 177 component_unbind_all(dev, drm_dev); 7db42e97bb41bd5 Daniel Vetter 2020-02-27 178 err_iommu_cleanup: ccea91998c8f140 Jeffy Chen 2017-04-06 179 rockchip_iommu_cleanup(drm_dev); f706974a69b6e2b Tomeu Vizoso 2016-06-10 180 err_free: 574e0fbfc95e7fc Thomas Zimmermann 2018-07-17 181 drm_dev_put(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 182 return ret; 2048e3286f347db Mark Yao 2014-08-22 183 } 2048e3286f347db Mark Yao 2014-08-22 184
:::::: The code at line 147 was first introduced by commit :::::: ccea91998c8f140bc3e324bbb3c3fb7148e72d31 drm/rockchip: Reorder drm bind/unbind sequence
:::::: TO: Jeffy Chen jeffy.chen@rock-chips.com :::::: CC: Sean Paul seanpaul@chromium.org
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Drat I butchered this. Will fix for next round and actually compile-test arm again :-/ -Daniel
On Fri, Feb 28, 2020 at 10:19 PM kbuild test robot lkp@intel.com wrote:
Hi Daniel,
I love your patch! Yet something to improve:
[auto build test ERROR on drm-tip/drm-tip] [also build test ERROR on next-20200228] [cannot apply to drm-intel/for-linux-next linus/master pinchartl-media/drm/du/next v5.6-rc3] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Daniel-Vetter/drm-managed-resources... base: git://anongit.freedesktop.org/drm/drm-tip drm-tip config: arm64-defconfig (attached as .config) compiler: aarch64-linux-gcc (GCC) 7.5.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.5.0 make.cross ARCH=arm64
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
drivers/gpu/drm/rockchip/rockchip_drm_drv.c: In function 'rockchip_drm_bind':
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:147:3: error: label 'err_mode_config_cleanup' used but not defined
goto err_mode_config_cleanup; ^~~~
vim +/err_mode_config_cleanup +147 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
2048e3286f347db Mark Yao 2014-08-22 110 f706974a69b6e2b Tomeu Vizoso 2016-06-10 111 static int rockchip_drm_bind(struct device *dev) 2048e3286f347db Mark Yao 2014-08-22 112 { f706974a69b6e2b Tomeu Vizoso 2016-06-10 113 struct drm_device *drm_dev; 2048e3286f347db Mark Yao 2014-08-22 114 struct rockchip_drm_private *private; 2048e3286f347db Mark Yao 2014-08-22 115 int ret; 2048e3286f347db Mark Yao 2014-08-22 116 f706974a69b6e2b Tomeu Vizoso 2016-06-10 117 drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); 0f2886057be322d Tom Gundersen 2016-09-21 118 if (IS_ERR(drm_dev)) 0f2886057be322d Tom Gundersen 2016-09-21 119 return PTR_ERR(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 120 f706974a69b6e2b Tomeu Vizoso 2016-06-10 121 dev_set_drvdata(dev, drm_dev); f706974a69b6e2b Tomeu Vizoso 2016-06-10 122 f706974a69b6e2b Tomeu Vizoso 2016-06-10 123 private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); f706974a69b6e2b Tomeu Vizoso 2016-06-10 124 if (!private) { f706974a69b6e2b Tomeu Vizoso 2016-06-10 125 ret = -ENOMEM; 9127f99c4801f32 Tomasz Figa 2016-06-21 126 goto err_free; f706974a69b6e2b Tomeu Vizoso 2016-06-10 127 } f706974a69b6e2b Tomeu Vizoso 2016-06-10 128 2048e3286f347db Mark Yao 2014-08-22 129 drm_dev->dev_private = private; 2048e3286f347db Mark Yao 2014-08-22 130 5182c1a556d7ff7 Yakir Yang 2016-07-24 131 INIT_LIST_HEAD(&private->psr_list); 60beeccc72cabef Sean Paul 2018-03-05 132 mutex_init(&private->psr_list_lock); 5182c1a556d7ff7 Yakir Yang 2016-07-24 133 ccea91998c8f140 Jeffy Chen 2017-04-06 134 ret = rockchip_drm_init_iommu(drm_dev); ccea91998c8f140 Jeffy Chen 2017-04-06 135 if (ret) ccea91998c8f140 Jeffy Chen 2017-04-06 136 goto err_free; ccea91998c8f140 Jeffy Chen 2017-04-06 137 7db42e97bb41bd5 Daniel Vetter 2020-02-27 138 ret = drm_mode_config_init(drm_dev); 7db42e97bb41bd5 Daniel Vetter 2020-02-27 139 if (ret) 7db42e97bb41bd5 Daniel Vetter 2020-02-27 140 goto err_iommu_cleanup; 2048e3286f347db Mark Yao 2014-08-22 141 2048e3286f347db Mark Yao 2014-08-22 142 rockchip_drm_mode_config_init(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 143 2048e3286f347db Mark Yao 2014-08-22 144 /* Try to bind all sub drivers. */ 2048e3286f347db Mark Yao 2014-08-22 145 ret = component_bind_all(dev, drm_dev); 2048e3286f347db Mark Yao 2014-08-22 146 if (ret) ccea91998c8f140 Jeffy Chen 2017-04-06 @147 goto err_mode_config_cleanup; 2048e3286f347db Mark Yao 2014-08-22 148 ccea91998c8f140 Jeffy Chen 2017-04-06 149 ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); ccea91998c8f140 Jeffy Chen 2017-04-06 150 if (ret) ccea91998c8f140 Jeffy Chen 2017-04-06 151 goto err_unbind_all; ccea91998c8f140 Jeffy Chen 2017-04-06 152 ccea91998c8f140 Jeffy Chen 2017-04-06 153 drm_mode_config_reset(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 154 2048e3286f347db Mark Yao 2014-08-22 155 /* 2048e3286f347db Mark Yao 2014-08-22 156 * enable drm irq mode. 2048e3286f347db Mark Yao 2014-08-22 157 * - with irq_enabled = true, we can use the vblank feature. 2048e3286f347db Mark Yao 2014-08-22 158 */ 2048e3286f347db Mark Yao 2014-08-22 159 drm_dev->irq_enabled = true; 2048e3286f347db Mark Yao 2014-08-22 160 2048e3286f347db Mark Yao 2014-08-22 161 ret = rockchip_drm_fbdev_init(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 162 if (ret) 8415ab565da966b Mark Yao 2017-08-01 163 goto err_unbind_all; 8415ab565da966b Mark Yao 2017-08-01 164 8415ab565da966b Mark Yao 2017-08-01 165 /* init kms poll for handling hpd */ 8415ab565da966b Mark Yao 2017-08-01 166 drm_kms_helper_poll_init(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 167 9127f99c4801f32 Tomasz Figa 2016-06-21 168 ret = drm_dev_register(drm_dev, 0); 9127f99c4801f32 Tomasz Figa 2016-06-21 169 if (ret) 8415ab565da966b Mark Yao 2017-08-01 170 goto err_kms_helper_poll_fini; 9127f99c4801f32 Tomasz Figa 2016-06-21 171 2048e3286f347db Mark Yao 2014-08-22 172 return 0; 2048e3286f347db Mark Yao 2014-08-22 173 err_kms_helper_poll_fini: 2048e3286f347db Mark Yao 2014-08-22 174 drm_kms_helper_poll_fini(drm_dev); 8415ab565da966b Mark Yao 2017-08-01 175 rockchip_drm_fbdev_fini(drm_dev); ccea91998c8f140 Jeffy Chen 2017-04-06 176 err_unbind_all: 2048e3286f347db Mark Yao 2014-08-22 177 component_unbind_all(dev, drm_dev); 7db42e97bb41bd5 Daniel Vetter 2020-02-27 178 err_iommu_cleanup: ccea91998c8f140 Jeffy Chen 2017-04-06 179 rockchip_iommu_cleanup(drm_dev); f706974a69b6e2b Tomeu Vizoso 2016-06-10 180 err_free: 574e0fbfc95e7fc Thomas Zimmermann 2018-07-17 181 drm_dev_put(drm_dev); 2048e3286f347db Mark Yao 2014-08-22 182 return ret; 2048e3286f347db Mark Yao 2014-08-22 183 } 2048e3286f347db Mark Yao 2014-08-22 184
:::::: The code at line 147 was first introduced by commit :::::: ccea91998c8f140bc3e324bbb3c3fb7148e72d31 drm/rockchip: Reorder drm bind/unbind sequence
:::::: TO: Jeffy Chen jeffy.chen@rock-chips.com :::::: CC: Sean Paul seanpaul@chromium.org
0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Daniel,
I love your patch! Yet something to improve:
[auto build test ERROR on drm-tip/drm-tip] [also build test ERROR on next-20200228] [cannot apply to drm-intel/for-linux-next linus/master pinchartl-media/drm/du/next v5.6-rc3] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Daniel-Vetter/drm-managed-resources... base: git://anongit.freedesktop.org/drm/drm-tip drm-tip config: arm64-defconfig (attached as .config) compiler: clang version 11.0.0 (git://gitmirror/llvm_project 949134e2fefd34a38ed71de90dffe2300e2e1139) reproduce: # FIXME the reproduce steps for clang is not ready yet
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:147:8: error: use of undeclared label 'err_mode_config_cleanup'
goto err_mode_config_cleanup; ^ 1 error generated.
vim +/err_mode_config_cleanup +147 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
2048e3286f347d Mark Yao 2014-08-22 110 f706974a69b6e2 Tomeu Vizoso 2016-06-10 111 static int rockchip_drm_bind(struct device *dev) 2048e3286f347d Mark Yao 2014-08-22 112 { f706974a69b6e2 Tomeu Vizoso 2016-06-10 113 struct drm_device *drm_dev; 2048e3286f347d Mark Yao 2014-08-22 114 struct rockchip_drm_private *private; 2048e3286f347d Mark Yao 2014-08-22 115 int ret; 2048e3286f347d Mark Yao 2014-08-22 116 f706974a69b6e2 Tomeu Vizoso 2016-06-10 117 drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); 0f2886057be322 Tom Gundersen 2016-09-21 118 if (IS_ERR(drm_dev)) 0f2886057be322 Tom Gundersen 2016-09-21 119 return PTR_ERR(drm_dev); 2048e3286f347d Mark Yao 2014-08-22 120 f706974a69b6e2 Tomeu Vizoso 2016-06-10 121 dev_set_drvdata(dev, drm_dev); f706974a69b6e2 Tomeu Vizoso 2016-06-10 122 f706974a69b6e2 Tomeu Vizoso 2016-06-10 123 private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); f706974a69b6e2 Tomeu Vizoso 2016-06-10 124 if (!private) { f706974a69b6e2 Tomeu Vizoso 2016-06-10 125 ret = -ENOMEM; 9127f99c4801f3 Tomasz Figa 2016-06-21 126 goto err_free; f706974a69b6e2 Tomeu Vizoso 2016-06-10 127 } f706974a69b6e2 Tomeu Vizoso 2016-06-10 128 2048e3286f347d Mark Yao 2014-08-22 129 drm_dev->dev_private = private; 2048e3286f347d Mark Yao 2014-08-22 130 5182c1a556d7ff Yakir Yang 2016-07-24 131 INIT_LIST_HEAD(&private->psr_list); 60beeccc72cabe Sean Paul 2018-03-05 132 mutex_init(&private->psr_list_lock); 5182c1a556d7ff Yakir Yang 2016-07-24 133 ccea91998c8f14 Jeffy Chen 2017-04-06 134 ret = rockchip_drm_init_iommu(drm_dev); ccea91998c8f14 Jeffy Chen 2017-04-06 135 if (ret) ccea91998c8f14 Jeffy Chen 2017-04-06 136 goto err_free; ccea91998c8f14 Jeffy Chen 2017-04-06 137 7db42e97bb41bd Daniel Vetter 2020-02-27 138 ret = drm_mode_config_init(drm_dev); 7db42e97bb41bd Daniel Vetter 2020-02-27 139 if (ret) 7db42e97bb41bd Daniel Vetter 2020-02-27 140 goto err_iommu_cleanup; 2048e3286f347d Mark Yao 2014-08-22 141 2048e3286f347d Mark Yao 2014-08-22 142 rockchip_drm_mode_config_init(drm_dev); 2048e3286f347d Mark Yao 2014-08-22 143 2048e3286f347d Mark Yao 2014-08-22 144 /* Try to bind all sub drivers. */ 2048e3286f347d Mark Yao 2014-08-22 145 ret = component_bind_all(dev, drm_dev); 2048e3286f347d Mark Yao 2014-08-22 146 if (ret) ccea91998c8f14 Jeffy Chen 2017-04-06 @147 goto err_mode_config_cleanup; 2048e3286f347d Mark Yao 2014-08-22 148 ccea91998c8f14 Jeffy Chen 2017-04-06 149 ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); ccea91998c8f14 Jeffy Chen 2017-04-06 150 if (ret) ccea91998c8f14 Jeffy Chen 2017-04-06 151 goto err_unbind_all; ccea91998c8f14 Jeffy Chen 2017-04-06 152 ccea91998c8f14 Jeffy Chen 2017-04-06 153 drm_mode_config_reset(drm_dev); 2048e3286f347d Mark Yao 2014-08-22 154 2048e3286f347d Mark Yao 2014-08-22 155 /* 2048e3286f347d Mark Yao 2014-08-22 156 * enable drm irq mode. 2048e3286f347d Mark Yao 2014-08-22 157 * - with irq_enabled = true, we can use the vblank feature. 2048e3286f347d Mark Yao 2014-08-22 158 */ 2048e3286f347d Mark Yao 2014-08-22 159 drm_dev->irq_enabled = true; 2048e3286f347d Mark Yao 2014-08-22 160 2048e3286f347d Mark Yao 2014-08-22 161 ret = rockchip_drm_fbdev_init(drm_dev); 2048e3286f347d Mark Yao 2014-08-22 162 if (ret) 8415ab565da966 Mark Yao 2017-08-01 163 goto err_unbind_all; 8415ab565da966 Mark Yao 2017-08-01 164 8415ab565da966 Mark Yao 2017-08-01 165 /* init kms poll for handling hpd */ 8415ab565da966 Mark Yao 2017-08-01 166 drm_kms_helper_poll_init(drm_dev); 2048e3286f347d Mark Yao 2014-08-22 167 9127f99c4801f3 Tomasz Figa 2016-06-21 168 ret = drm_dev_register(drm_dev, 0); 9127f99c4801f3 Tomasz Figa 2016-06-21 169 if (ret) 8415ab565da966 Mark Yao 2017-08-01 170 goto err_kms_helper_poll_fini; 9127f99c4801f3 Tomasz Figa 2016-06-21 171 2048e3286f347d Mark Yao 2014-08-22 172 return 0; 2048e3286f347d Mark Yao 2014-08-22 173 err_kms_helper_poll_fini: 2048e3286f347d Mark Yao 2014-08-22 174 drm_kms_helper_poll_fini(drm_dev); 8415ab565da966 Mark Yao 2017-08-01 175 rockchip_drm_fbdev_fini(drm_dev); ccea91998c8f14 Jeffy Chen 2017-04-06 176 err_unbind_all: 2048e3286f347d Mark Yao 2014-08-22 177 component_unbind_all(dev, drm_dev); 7db42e97bb41bd Daniel Vetter 2020-02-27 178 err_iommu_cleanup: ccea91998c8f14 Jeffy Chen 2017-04-06 179 rockchip_iommu_cleanup(drm_dev); f706974a69b6e2 Tomeu Vizoso 2016-06-10 180 err_free: 574e0fbfc95e7f Thomas Zimmermann 2018-07-17 181 drm_dev_put(drm_dev); 2048e3286f347d Mark Yao 2014-08-22 182 return ret; 2048e3286f347d Mark Yao 2014-08-22 183 } 2048e3286f347d Mark Yao 2014-08-22 184
:::::: The code at line 147 was first introduced by commit :::::: ccea91998c8f140bc3e324bbb3c3fb7148e72d31 drm/rockchip: Reorder drm bind/unbind sequence
:::::: TO: Jeffy Chen jeffy.chen@rock-chips.com :::::: CC: Sean Paul seanpaul@chromium.org
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
It's right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: Another driver with a bit much devm_kzalloc, which should probably use drmm_kzalloc instead ...
v2: Explain why this cleanup is possible (Laurent).
Acked-by: Philippe Cornu philippe.cornu@st.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Yannick Fertre yannick.fertre@st.com Cc: Philippe Cornu philippe.cornu@st.com Cc: Benjamin Gaignard benjamin.gaignard@linaro.org Cc: Vincent Abriou vincent.abriou@st.com Cc: Maxime Coquelin mcoquelin.stm32@gmail.com Cc: Alexandre Torgue alexandre.torgue@st.com Cc: linux-stm32@st-md-mailman.stormreply.com Cc: linux-arm-kernel@lists.infradead.org --- drivers/gpu/drm/stm/drv.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index ea9fcbdc68b3..5b374531dd8c 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -88,7 +88,9 @@ static int drv_load(struct drm_device *ddev)
ddev->dev_private = (void *)ldev;
- drm_mode_config_init(ddev); + ret = drm_mode_config_init(ddev); + if (ret) + return ret;
/* * set max width and height as default value. @@ -103,7 +105,7 @@ static int drv_load(struct drm_device *ddev)
ret = ltdc_load(ddev); if (ret) - goto err; + return ret;
drm_mode_config_reset(ddev); drm_kms_helper_poll_init(ddev); @@ -111,9 +113,6 @@ static int drv_load(struct drm_device *ddev) platform_set_drvdata(pdev, ddev);
return 0; -err: - drm_mode_config_cleanup(ddev); - return ret; }
static void drv_unload(struct drm_device *ddev) @@ -122,7 +121,6 @@ static void drv_unload(struct drm_device *ddev)
drm_kms_helper_poll_fini(ddev); ltdc_unload(ddev); - drm_mode_config_cleanup(ddev); }
static __maybe_unused int drv_suspend(struct device *dev)
It's right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: Another driver with a bit much devm_kzalloc, which should probably use drmm_kzalloc instead ...
v2: Explain why this cleanup is possible (Laurent).
Reviewed-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Kieran Bingham kieran.bingham+renesas@ideasonboard.com Cc: linux-renesas-soc@vger.kernel.org --- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 2 -- drivers/gpu/drm/shmobile/shmob_drm_kms.c | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index b8c0930959c7..ae9d6b8d3ca8 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -192,7 +192,6 @@ static int shmob_drm_remove(struct platform_device *pdev)
drm_dev_unregister(ddev); drm_kms_helper_poll_fini(ddev); - drm_mode_config_cleanup(ddev); drm_irq_uninstall(ddev); drm_dev_put(ddev);
@@ -288,7 +287,6 @@ static int shmob_drm_probe(struct platform_device *pdev) drm_irq_uninstall(ddev); err_modeset_cleanup: drm_kms_helper_poll_fini(ddev); - drm_mode_config_cleanup(ddev); err_free_drm_dev: drm_dev_put(ddev);
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.c b/drivers/gpu/drm/shmobile/shmob_drm_kms.c index c51197b6fd85..e6e34bb75ba0 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_kms.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_kms.c @@ -126,7 +126,11 @@ static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = {
int shmob_drm_modeset_init(struct shmob_drm_device *sdev) { - drm_mode_config_init(sdev->ddev); + int ret; + + ret = drm_mode_config_init(sdev->ddev); + if (ret) + return ret;
shmob_drm_crtc_create(sdev); shmob_drm_encoder_create(sdev);
It's right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: Another driver with a bit much devm_kzalloc, which should probably use drmm_kzalloc instead ...
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 0563c6813333..947b2cbe2836 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -162,7 +162,9 @@ static int mtk_drm_kms_init(struct drm_device *drm) } private->mutex_dev = &pdev->dev;
- drm_mode_config_init(drm); + ret = drm_mode_config_init(drm); + if (ret) + return ret;
drm->mode_config.min_width = 64; drm->mode_config.min_height = 64; @@ -179,7 +181,7 @@ static int mtk_drm_kms_init(struct drm_device *drm)
ret = component_bind_all(drm->dev, drm); if (ret) - goto err_config_cleanup; + return ret;
/* * We currently support two fixed data streams, each optional, @@ -255,8 +257,6 @@ static int mtk_drm_kms_init(struct drm_device *drm) dma_dev->dma_parms = NULL; err_component_unbind: component_unbind_all(drm->dev, drm); -err_config_cleanup: - drm_mode_config_cleanup(drm);
return ret; } @@ -272,7 +272,6 @@ static void mtk_drm_kms_deinit(struct drm_device *drm) private->dma_dev->dma_parms = NULL;
component_unbind_all(drm->dev, drm); - drm_mode_config_cleanup(drm); }
static const struct file_operations mtk_drm_fops = {
It's right above the drm_dev_put().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
Aside: Another driver with a bit much devm_kzalloc, which should probably use drmm_kzalloc instead ...
I'm pretty sure this one blows up already under KASAN because it's using devm_drm_dev_init, and later on devm_kzalloc. Hence the memory will get freed before the final drm_dev_put (all from the devres code), but the cleanup in that final drm_dev_put will access the just freed memory.
Unfortunately fixing this properly needs slightly more work, namely drmm_ versions for all the drm objects (planes, crtc, ...), so that the cleanup actually happens before even drmm_kzalloc would release the underlying memory. Not quite there yet.
v2: Explain why this cleanup is possible (Laurent).
Acked-by: Jyri Sarha jsarha@ti.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Jyri Sarha jsarha@ti.com Cc: Tomi Valkeinen tomi.valkeinen@ti.com --- drivers/gpu/drm/tidss/tidss_drv.c | 4 ---- drivers/gpu/drm/tidss/tidss_kms.c | 19 +++++-------------- drivers/gpu/drm/tidss/tidss_kms.h | 1 - 3 files changed, 5 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index 460d5e9d0cf4..ad449d104306 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -103,11 +103,7 @@ static const struct dev_pm_ops tidss_pm_ops = {
static void tidss_release(struct drm_device *ddev) { - struct tidss_device *tidss = ddev->dev_private; - drm_kms_helper_poll_fini(ddev); - - tidss_modeset_cleanup(tidss); }
DEFINE_DRM_GEM_CMA_FOPS(tidss_fops); diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index 3b6f8d54a016..d13b896f184a 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -208,7 +208,9 @@ int tidss_modeset_init(struct tidss_device *tidss)
dev_dbg(tidss->dev, "%s\n", __func__);
- drm_mode_config_init(ddev); + ret = drm_mode_config_init(ddev); + if (ret) + return ret;
ddev->mode_config.min_width = 8; ddev->mode_config.min_height = 8; @@ -220,11 +222,11 @@ int tidss_modeset_init(struct tidss_device *tidss)
ret = tidss_dispc_modeset_init(tidss); if (ret) - goto err_mode_config_cleanup; + return ret;
ret = drm_vblank_init(ddev, tidss->num_crtcs); if (ret) - goto err_mode_config_cleanup; + return ret;
/* Start with vertical blanking interrupt reporting disabled. */ for (i = 0; i < tidss->num_crtcs; ++i) @@ -235,15 +237,4 @@ int tidss_modeset_init(struct tidss_device *tidss) dev_dbg(tidss->dev, "%s done\n", __func__);
return 0; - -err_mode_config_cleanup: - drm_mode_config_cleanup(ddev); - return ret; -} - -void tidss_modeset_cleanup(struct tidss_device *tidss) -{ - struct drm_device *ddev = &tidss->ddev; - - drm_mode_config_cleanup(ddev); } diff --git a/drivers/gpu/drm/tidss/tidss_kms.h b/drivers/gpu/drm/tidss/tidss_kms.h index dda5625d0128..99aaff099f22 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.h +++ b/drivers/gpu/drm/tidss/tidss_kms.h @@ -10,6 +10,5 @@ struct tidss_device;
int tidss_modeset_init(struct tidss_device *tidss); -void tidss_modeset_cleanup(struct tidss_device *tidss);
#endif
The drm_mode_config_cleanup call we can drop, and all the allocations we can switch over to drmm_kzalloc. Unfortunately the work queue is still present, so can't get rid of the drm_driver->release function outright.
Reviewed-by: Hans de Goede hdegoede@redhat.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Hans de Goede hdegoede@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org --- drivers/gpu/drm/tiny/gm12u320.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 3928f69bbd3d..5bd26fc6fafa 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -160,7 +160,7 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320) int i, block_size; const char *hdr;
- gm12u320->cmd_buf = kmalloc(CMD_SIZE, GFP_KERNEL); + gm12u320->cmd_buf = drmm_kmalloc(&gm12u320->dev, CMD_SIZE, GFP_KERNEL); if (!gm12u320->cmd_buf) return -ENOMEM;
@@ -173,7 +173,8 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320) hdr = data_block_header; }
- gm12u320->data_buf[i] = kzalloc(block_size, GFP_KERNEL); + gm12u320->data_buf[i] = drmm_kzalloc(&gm12u320->dev, + block_size, GFP_KERNEL); if (!gm12u320->data_buf[i]) return -ENOMEM;
@@ -192,15 +193,8 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
static void gm12u320_usb_free(struct gm12u320_device *gm12u320) { - int i; - if (gm12u320->fb_update.workq) destroy_workqueue(gm12u320->fb_update.workq); - - for (i = 0; i < GM12U320_BLOCK_COUNT; i++) - kfree(gm12u320->data_buf[i]); - - kfree(gm12u320->cmd_buf); }
static int gm12u320_misc_request(struct gm12u320_device *gm12u320, @@ -636,7 +630,6 @@ static void gm12u320_driver_release(struct drm_device *dev) struct gm12u320_device *gm12u320 = dev->dev_private;
gm12u320_usb_free(gm12u320); - drm_mode_config_cleanup(dev); }
DEFINE_DRM_GEM_FOPS(gm12u320_fops); @@ -693,7 +686,10 @@ static int gm12u320_usb_probe(struct usb_interface *interface, dev->dev_private = gm12u320; drmm_add_final_kfree(dev, gm12u320);
- drm_mode_config_init(dev); + ret = drm_mode_config_init(dev); + if (ret) + goto err_put; + dev->mode_config.min_width = GM12U320_USER_WIDTH; dev->mode_config.max_width = GM12U320_USER_WIDTH; dev->mode_config.min_height = GM12U320_HEIGHT;
Only drops the drm_dev_put, but hey a few lines!
Reviewed-by: Hans de Goede hdegoede@redhat.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Hans de Goede hdegoede@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org --- drivers/gpu/drm/tiny/gm12u320.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 5bd26fc6fafa..65dfb87ccb13 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -678,7 +678,7 @@ static int gm12u320_usb_probe(struct usb_interface *interface, init_waitqueue_head(&gm12u320->fb_update.waitq);
dev = &gm12u320->dev; - ret = drm_dev_init(dev, &gm12u320_drm_driver, &interface->dev); + ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver); if (ret) { kfree(gm12u320); return ret; @@ -688,7 +688,7 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
ret = drm_mode_config_init(dev); if (ret) - goto err_put; + return ret;
dev->mode_config.min_width = GM12U320_USER_WIDTH; dev->mode_config.max_width = GM12U320_USER_WIDTH; @@ -698,15 +698,15 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
ret = gm12u320_usb_alloc(gm12u320); if (ret) - goto err_put; + return ret;
ret = gm12u320_set_ecomode(gm12u320); if (ret) - goto err_put; + return ret;
ret = gm12u320_conn_init(gm12u320); if (ret) - goto err_put; + return ret;
ret = drm_simple_display_pipe_init(&gm12u320->dev, &gm12u320->pipe, @@ -716,22 +716,18 @@ static int gm12u320_usb_probe(struct usb_interface *interface, gm12u320_pipe_modifiers, &gm12u320->conn); if (ret) - goto err_put; + return ret;
drm_mode_config_reset(dev);
usb_set_intfdata(interface, dev); ret = drm_dev_register(dev, 0); if (ret) - goto err_put; + return ret;
drm_fbdev_generic_setup(dev, 0);
return 0; - -err_put: - drm_dev_put(dev); - return ret; }
static void gm12u320_usb_disconnect(struct usb_interface *interface) @@ -741,7 +737,6 @@ static void gm12u320_usb_disconnect(struct usb_interface *interface)
gm12u320_stop_fb_update(gm12u320); drm_dev_unplug(dev); - drm_dev_put(dev); }
static __maybe_unused int gm12u320_suspend(struct usb_interface *interface,
Also there's a race in the disconnect implemenation. First shut down, then unplug, leaves a window where userspace could sneak in and restart the entire machinery.
With this we can also delete the very un-atomic global pipe_enabled tracking.
Reviewed-by: Hans de Goede hdegoede@redhat.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Hans de Goede hdegoede@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org --- drivers/gpu/drm/tiny/gm12u320.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 65dfb87ccb13..c22b2ee470eb 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -88,7 +88,6 @@ struct gm12u320_device { struct usb_device *udev; unsigned char *cmd_buf; unsigned char *data_buf[GM12U320_BLOCK_COUNT]; - bool pipe_enabled; struct { bool run; struct workqueue_struct *workq; @@ -589,7 +588,6 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
gm12u320_fb_mark_dirty(plane_state->fb, &rect); gm12u320_start_fb_update(gm12u320); - gm12u320->pipe_enabled = true; }
static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -597,7 +595,6 @@ static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe) struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private;
gm12u320_stop_fb_update(gm12u320); - gm12u320->pipe_enabled = false; }
static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe, @@ -733,22 +730,17 @@ static int gm12u320_usb_probe(struct usb_interface *interface, static void gm12u320_usb_disconnect(struct usb_interface *interface) { struct drm_device *dev = usb_get_intfdata(interface); - struct gm12u320_device *gm12u320 = dev->dev_private;
- gm12u320_stop_fb_update(gm12u320); drm_dev_unplug(dev); + drm_atomic_helper_shutdown(dev); }
static __maybe_unused int gm12u320_suspend(struct usb_interface *interface, pm_message_t message) { struct drm_device *dev = usb_get_intfdata(interface); - struct gm12u320_device *gm12u320 = dev->dev_private;
- if (gm12u320->pipe_enabled) - gm12u320_stop_fb_update(gm12u320); - - return 0; + return drm_mode_config_helper_suspend(dev); }
static __maybe_unused int gm12u320_resume(struct usb_interface *interface) @@ -757,10 +749,8 @@ static __maybe_unused int gm12u320_resume(struct usb_interface *interface) struct gm12u320_device *gm12u320 = dev->dev_private;
gm12u320_set_ecomode(gm12u320); - if (gm12u320->pipe_enabled) - gm12u320_start_fb_update(gm12u320);
- return 0; + return drm_mode_config_helper_resume(dev); }
static const struct usb_device_id id_table[] = {
Instead of having a work item that never stops (which really should be a kthread), with a dedicated workqueue to not upset anyone else, use a delayed work. A bunch of changes:
- We can throw out all the custom wakeup and requeue logic and state tracking. If we schedule the work with a 0 delay it'll get scheduled immediately.
- Persistent state (frame & draw_status_timeout) need to be moved out of the work.
- diff is bigger than the changes, biggest chunk is reindenting the work fn because it lost its while loop.
Lots of code deleting as consequence all over. Specifically we can delete the drm_driver.release code now!
v2: Review from Hans: - Use mod_delayed_work in the plane update path to make sure we do actually schedule immediately). In the worker we still want queue_delayed_work, which won't modify the timeout when the work is already scheduled. Which is exactly what we want if the work races with a plane update. - Switch to system_long_wq, Hans says on usb2 a plane upload can take 80 ms.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Hans de Goede hdegoede@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org --- drivers/gpu/drm/tiny/gm12u320.c | 171 +++++++++++++------------------- 1 file changed, 68 insertions(+), 103 deletions(-)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index c22b2ee470eb..bbc4ef4456e6 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -89,13 +89,12 @@ struct gm12u320_device { unsigned char *cmd_buf; unsigned char *data_buf[GM12U320_BLOCK_COUNT]; struct { - bool run; - struct workqueue_struct *workq; - struct work_struct work; - wait_queue_head_t waitq; + struct delayed_work work; struct mutex lock; struct drm_framebuffer *fb; struct drm_rect rect; + int frame; + int draw_status_timeout; } fb_update; };
@@ -183,19 +182,9 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320) data_block_footer, DATA_BLOCK_FOOTER_SIZE); }
- gm12u320->fb_update.workq = create_singlethread_workqueue(DRIVER_NAME); - if (!gm12u320->fb_update.workq) - return -ENOMEM; - return 0; }
-static void gm12u320_usb_free(struct gm12u320_device *gm12u320) -{ - if (gm12u320->fb_update.workq) - destroy_workqueue(gm12u320->fb_update.workq); -} - static int gm12u320_misc_request(struct gm12u320_device *gm12u320, u8 req_a, u8 req_b, u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d) @@ -338,80 +327,77 @@ static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320) static void gm12u320_fb_update_work(struct work_struct *work) { struct gm12u320_device *gm12u320 = - container_of(work, struct gm12u320_device, fb_update.work); - int draw_status_timeout = FIRST_FRAME_TIMEOUT; + container_of(to_delayed_work(work), struct gm12u320_device, + fb_update.work); int block, block_size, len; - int frame = 0; int ret = 0;
- while (gm12u320->fb_update.run) { - gm12u320_copy_fb_to_blocks(gm12u320); - - for (block = 0; block < GM12U320_BLOCK_COUNT; block++) { - if (block == GM12U320_BLOCK_COUNT - 1) - block_size = DATA_LAST_BLOCK_SIZE; - else - block_size = DATA_BLOCK_SIZE; - - /* Send data command to device */ - memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE); - gm12u320->cmd_buf[8] = block_size & 0xff; - gm12u320->cmd_buf[9] = block_size >> 8; - gm12u320->cmd_buf[20] = 0xfc - block * 4; - gm12u320->cmd_buf[21] = block | (frame << 7); - - ret = usb_bulk_msg(gm12u320->udev, - usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), - gm12u320->cmd_buf, CMD_SIZE, &len, - CMD_TIMEOUT); - if (ret || len != CMD_SIZE) - goto err; - - /* Send data block to device */ - ret = usb_bulk_msg(gm12u320->udev, - usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), - gm12u320->data_buf[block], block_size, - &len, DATA_TIMEOUT); - if (ret || len != block_size) - goto err; - - /* Read status */ - ret = usb_bulk_msg(gm12u320->udev, - usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), - gm12u320->cmd_buf, READ_STATUS_SIZE, &len, - CMD_TIMEOUT); - if (ret || len != READ_STATUS_SIZE) - goto err; - } + gm12u320_copy_fb_to_blocks(gm12u320); + + for (block = 0; block < GM12U320_BLOCK_COUNT; block++) { + if (block == GM12U320_BLOCK_COUNT - 1) + block_size = DATA_LAST_BLOCK_SIZE; + else + block_size = DATA_BLOCK_SIZE; + + /* Send data command to device */ + memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE); + gm12u320->cmd_buf[8] = block_size & 0xff; + gm12u320->cmd_buf[9] = block_size >> 8; + gm12u320->cmd_buf[20] = 0xfc - block * 4; + gm12u320->cmd_buf[21] = + block | (gm12u320->fb_update.frame << 7);
- /* Send draw command to device */ - memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE); ret = usb_bulk_msg(gm12u320->udev, usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), - gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT); + gm12u320->cmd_buf, CMD_SIZE, &len, + CMD_TIMEOUT); if (ret || len != CMD_SIZE) goto err;
+ /* Send data block to device */ + ret = usb_bulk_msg(gm12u320->udev, + usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), + gm12u320->data_buf[block], block_size, + &len, DATA_TIMEOUT); + if (ret || len != block_size) + goto err; + /* Read status */ ret = usb_bulk_msg(gm12u320->udev, usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), gm12u320->cmd_buf, READ_STATUS_SIZE, &len, - draw_status_timeout); + CMD_TIMEOUT); if (ret || len != READ_STATUS_SIZE) goto err; - - draw_status_timeout = CMD_TIMEOUT; - frame = !frame; - - /* - * We must draw a frame every 2s otherwise the projector - * switches back to showing its logo. - */ - wait_event_timeout(gm12u320->fb_update.waitq, - !gm12u320->fb_update.run || - gm12u320->fb_update.fb != NULL, - IDLE_TIMEOUT); } + + /* Send draw command to device */ + memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE); + ret = usb_bulk_msg(gm12u320->udev, + usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), + gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT); + if (ret || len != CMD_SIZE) + goto err; + + /* Read status */ + ret = usb_bulk_msg(gm12u320->udev, + usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), + gm12u320->cmd_buf, READ_STATUS_SIZE, &len, + gm12u320->fb_update.draw_status_timeout); + if (ret || len != READ_STATUS_SIZE) + goto err; + + gm12u320->fb_update.draw_status_timeout = CMD_TIMEOUT; + gm12u320->fb_update.frame = !gm12u320->fb_update.frame; + + /* + * We must draw a frame every 2s otherwise the projector + * switches back to showing its logo. + */ + queue_delayed_work(system_long_wq, &gm12u320->fb_update.work, + IDLE_TIMEOUT); + return; err: /* Do not log errors caused by module unload or device unplug */ @@ -446,36 +432,24 @@ static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb, mutex_unlock(&gm12u320->fb_update.lock);
if (wakeup) - wake_up(&gm12u320->fb_update.waitq); + mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
if (old_fb) drm_framebuffer_put(old_fb); }
-static void gm12u320_start_fb_update(struct gm12u320_device *gm12u320) -{ - mutex_lock(&gm12u320->fb_update.lock); - gm12u320->fb_update.run = true; - mutex_unlock(&gm12u320->fb_update.lock); - - queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work); -} - static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320) { - mutex_lock(&gm12u320->fb_update.lock); - gm12u320->fb_update.run = false; - mutex_unlock(&gm12u320->fb_update.lock); + struct drm_framebuffer *old_fb;
- wake_up(&gm12u320->fb_update.waitq); - cancel_work_sync(&gm12u320->fb_update.work); + cancel_delayed_work_sync(&gm12u320->fb_update.work);
mutex_lock(&gm12u320->fb_update.lock); - if (gm12u320->fb_update.fb) { - drm_framebuffer_put(gm12u320->fb_update.fb); - gm12u320->fb_update.fb = NULL; - } + old_fb = gm12u320->fb_update.fb; + gm12u320->fb_update.fb = NULL; mutex_unlock(&gm12u320->fb_update.lock); + + drm_framebuffer_put(old_fb); }
static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320) @@ -583,11 +557,11 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) { - struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT }; + struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private;
+ gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT; gm12u320_fb_mark_dirty(plane_state->fb, &rect); - gm12u320_start_fb_update(gm12u320); }
static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe) @@ -622,13 +596,6 @@ static const uint64_t gm12u320_pipe_modifiers[] = { DRM_FORMAT_MOD_INVALID };
-static void gm12u320_driver_release(struct drm_device *dev) -{ - struct gm12u320_device *gm12u320 = dev->dev_private; - - gm12u320_usb_free(gm12u320); -} - DEFINE_DRM_GEM_FOPS(gm12u320_fops);
static struct drm_driver gm12u320_drm_driver = { @@ -640,7 +607,6 @@ static struct drm_driver gm12u320_drm_driver = { .major = DRIVER_MAJOR, .minor = DRIVER_MINOR,
- .release = gm12u320_driver_release, .fops = &gm12u320_fops, DRM_GEM_SHMEM_DRIVER_OPS, }; @@ -670,9 +636,8 @@ static int gm12u320_usb_probe(struct usb_interface *interface, return -ENOMEM;
gm12u320->udev = interface_to_usbdev(interface); - INIT_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work); + INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work); mutex_init(&gm12u320->fb_update.lock); - init_waitqueue_head(&gm12u320->fb_update.waitq);
dev = &gm12u320->dev; ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver);
Hi,
On 2/27/20 7:15 PM, Daniel Vetter wrote:
Instead of having a work item that never stops (which really should be a kthread), with a dedicated workqueue to not upset anyone else, use a delayed work. A bunch of changes:
We can throw out all the custom wakeup and requeue logic and state tracking. If we schedule the work with a 0 delay it'll get scheduled immediately.
Persistent state (frame & draw_status_timeout) need to be moved out of the work.
diff is bigger than the changes, biggest chunk is reindenting the work fn because it lost its while loop.
Lots of code deleting as consequence all over. Specifically we can delete the drm_driver.release code now!
v2: Review from Hans:
- Use mod_delayed_work in the plane update path to make sure we do actually schedule immediately). In the worker we still want queue_delayed_work, which won't modify the timeout when the work is already scheduled. Which is exactly what we want if the work races with a plane update.
- Switch to system_long_wq, Hans says on usb2 a plane upload can take 80 ms.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Hans de Goede hdegoede@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org
Patch looks good to me:
Reviewed-by: Hans de Goede hdegoede@redhat.com
Regards,
Hans
drivers/gpu/drm/tiny/gm12u320.c | 171 +++++++++++++------------------- 1 file changed, 68 insertions(+), 103 deletions(-)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index c22b2ee470eb..bbc4ef4456e6 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -89,13 +89,12 @@ struct gm12u320_device { unsigned char *cmd_buf; unsigned char *data_buf[GM12U320_BLOCK_COUNT]; struct {
bool run;
struct workqueue_struct *workq;
struct work_struct work;
wait_queue_head_t waitq;
struct mutex lock; struct drm_framebuffer *fb; struct drm_rect rect;struct delayed_work work;
int frame;
} fb_update; };int draw_status_timeout;
@@ -183,19 +182,9 @@ static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320) data_block_footer, DATA_BLOCK_FOOTER_SIZE); }
- gm12u320->fb_update.workq = create_singlethread_workqueue(DRIVER_NAME);
- if (!gm12u320->fb_update.workq)
return -ENOMEM;
- return 0; }
-static void gm12u320_usb_free(struct gm12u320_device *gm12u320) -{
- if (gm12u320->fb_update.workq)
destroy_workqueue(gm12u320->fb_update.workq);
-}
- static int gm12u320_misc_request(struct gm12u320_device *gm12u320, u8 req_a, u8 req_b, u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d)
@@ -338,80 +327,77 @@ static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320) static void gm12u320_fb_update_work(struct work_struct *work) { struct gm12u320_device *gm12u320 =
container_of(work, struct gm12u320_device, fb_update.work);
- int draw_status_timeout = FIRST_FRAME_TIMEOUT;
container_of(to_delayed_work(work), struct gm12u320_device,
int block, block_size, len;fb_update.work);
int frame = 0; int ret = 0;
while (gm12u320->fb_update.run) {
gm12u320_copy_fb_to_blocks(gm12u320);
for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
if (block == GM12U320_BLOCK_COUNT - 1)
block_size = DATA_LAST_BLOCK_SIZE;
else
block_size = DATA_BLOCK_SIZE;
/* Send data command to device */
memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
gm12u320->cmd_buf[8] = block_size & 0xff;
gm12u320->cmd_buf[9] = block_size >> 8;
gm12u320->cmd_buf[20] = 0xfc - block * 4;
gm12u320->cmd_buf[21] = block | (frame << 7);
ret = usb_bulk_msg(gm12u320->udev,
usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
gm12u320->cmd_buf, CMD_SIZE, &len,
CMD_TIMEOUT);
if (ret || len != CMD_SIZE)
goto err;
/* Send data block to device */
ret = usb_bulk_msg(gm12u320->udev,
usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
gm12u320->data_buf[block], block_size,
&len, DATA_TIMEOUT);
if (ret || len != block_size)
goto err;
/* Read status */
ret = usb_bulk_msg(gm12u320->udev,
usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
CMD_TIMEOUT);
if (ret || len != READ_STATUS_SIZE)
goto err;
}
- gm12u320_copy_fb_to_blocks(gm12u320);
- for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
if (block == GM12U320_BLOCK_COUNT - 1)
block_size = DATA_LAST_BLOCK_SIZE;
else
block_size = DATA_BLOCK_SIZE;
/* Send data command to device */
memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
gm12u320->cmd_buf[8] = block_size & 0xff;
gm12u320->cmd_buf[9] = block_size >> 8;
gm12u320->cmd_buf[20] = 0xfc - block * 4;
gm12u320->cmd_buf[21] =
block | (gm12u320->fb_update.frame << 7);
/* Send draw command to device */
ret = usb_bulk_msg(gm12u320->udev, usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
gm12u320->cmd_buf, CMD_SIZE, &len,
CMD_TIMEOUT);
if (ret || len != CMD_SIZE) goto err;
/* Send data block to device */
ret = usb_bulk_msg(gm12u320->udev,
usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
gm12u320->data_buf[block], block_size,
&len, DATA_TIMEOUT);
if (ret || len != block_size)
goto err;
/* Read status */ ret = usb_bulk_msg(gm12u320->udev, usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
draw_status_timeout);
if (ret || len != READ_STATUS_SIZE) goto err;CMD_TIMEOUT);
draw_status_timeout = CMD_TIMEOUT;
frame = !frame;
/*
* We must draw a frame every 2s otherwise the projector
* switches back to showing its logo.
*/
wait_event_timeout(gm12u320->fb_update.waitq,
!gm12u320->fb_update.run ||
gm12u320->fb_update.fb != NULL,
}IDLE_TIMEOUT);
- /* Send draw command to device */
- memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
- ret = usb_bulk_msg(gm12u320->udev,
usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
- if (ret || len != CMD_SIZE)
goto err;
- /* Read status */
- ret = usb_bulk_msg(gm12u320->udev,
usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
gm12u320->fb_update.draw_status_timeout);
- if (ret || len != READ_STATUS_SIZE)
goto err;
- gm12u320->fb_update.draw_status_timeout = CMD_TIMEOUT;
- gm12u320->fb_update.frame = !gm12u320->fb_update.frame;
- /*
* We must draw a frame every 2s otherwise the projector
* switches back to showing its logo.
*/
- queue_delayed_work(system_long_wq, &gm12u320->fb_update.work,
IDLE_TIMEOUT);
- return; err: /* Do not log errors caused by module unload or device unplug */
@@ -446,36 +432,24 @@ static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb, mutex_unlock(&gm12u320->fb_update.lock);
if (wakeup)
wake_up(&gm12u320->fb_update.waitq);
mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
if (old_fb) drm_framebuffer_put(old_fb); }
-static void gm12u320_start_fb_update(struct gm12u320_device *gm12u320) -{
- mutex_lock(&gm12u320->fb_update.lock);
- gm12u320->fb_update.run = true;
- mutex_unlock(&gm12u320->fb_update.lock);
- queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work);
-}
- static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320) {
- mutex_lock(&gm12u320->fb_update.lock);
- gm12u320->fb_update.run = false;
- mutex_unlock(&gm12u320->fb_update.lock);
- struct drm_framebuffer *old_fb;
- wake_up(&gm12u320->fb_update.waitq);
- cancel_work_sync(&gm12u320->fb_update.work);
cancel_delayed_work_sync(&gm12u320->fb_update.work);
mutex_lock(&gm12u320->fb_update.lock);
- if (gm12u320->fb_update.fb) {
drm_framebuffer_put(gm12u320->fb_update.fb);
gm12u320->fb_update.fb = NULL;
- }
old_fb = gm12u320->fb_update.fb;
gm12u320->fb_update.fb = NULL; mutex_unlock(&gm12u320->fb_update.lock);
drm_framebuffer_put(old_fb); }
static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
@@ -583,11 +557,11 @@ static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) {
- struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private;
gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT; gm12u320_fb_mark_dirty(plane_state->fb, &rect);
gm12u320_start_fb_update(gm12u320); }
static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
@@ -622,13 +596,6 @@ static const uint64_t gm12u320_pipe_modifiers[] = { DRM_FORMAT_MOD_INVALID };
-static void gm12u320_driver_release(struct drm_device *dev) -{
- struct gm12u320_device *gm12u320 = dev->dev_private;
- gm12u320_usb_free(gm12u320);
-}
DEFINE_DRM_GEM_FOPS(gm12u320_fops);
static struct drm_driver gm12u320_drm_driver = {
@@ -640,7 +607,6 @@ static struct drm_driver gm12u320_drm_driver = { .major = DRIVER_MAJOR, .minor = DRIVER_MINOR,
- .release = gm12u320_driver_release, .fops = &gm12u320_fops, DRM_GEM_SHMEM_DRIVER_OPS, };
@@ -670,9 +636,8 @@ static int gm12u320_usb_probe(struct usb_interface *interface, return -ENOMEM;
gm12u320->udev = interface_to_usbdev(interface);
- INIT_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
- INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work); mutex_init(&gm12u320->fb_update.lock);
init_waitqueue_head(&gm12u320->fb_update.waitq);
dev = &gm12u320->dev; ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver);
Allows us to drop the drm_driver.release callback.
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: "Noralf Trønnes" noralf@tronnes.org --- drivers/gpu/drm/tiny/repaper.c | 8 -------- 1 file changed, 8 deletions(-)
diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index 4741ff670ec9..2f70fb1be200 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -909,13 +909,6 @@ static const struct drm_mode_config_funcs repaper_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, };
-static void repaper_release(struct drm_device *drm) -{ - DRM_DEBUG_DRIVER("\n"); - - drm_mode_config_cleanup(drm); -} - static const uint32_t repaper_formats[] = { DRM_FORMAT_XRGB8888, }; @@ -953,7 +946,6 @@ DEFINE_DRM_GEM_CMA_FOPS(repaper_fops); static struct drm_driver repaper_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &repaper_fops, - .release = repaper_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .name = "repaper", .desc = "Pervasive Displays RePaper e-ink panels",
7/7 drivers agree that's the right choice, let's do this.
This avoids duplicating the same old error checking code over all 7 drivers, which is the motivation here.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Tested-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maxime Ripard mripard@kernel.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Eric Anholt eric@anholt.net Cc: David Lechner david@lechnology.com Cc: Kamlesh Gurudasani kamlesh.gurudasani@gmail.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org --- drivers/gpu/drm/drm_mipi_dbi.c | 4 ++++ drivers/gpu/drm/tiny/hx8357d.c | 2 -- drivers/gpu/drm/tiny/ili9225.c | 2 -- drivers/gpu/drm/tiny/ili9341.c | 2 -- drivers/gpu/drm/tiny/ili9486.c | 2 -- drivers/gpu/drm/tiny/mi0283qt.c | 2 -- drivers/gpu/drm/tiny/st7586.c | 2 -- drivers/gpu/drm/tiny/st7735r.c | 2 -- 8 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index a678e07508d4..9de1586659be 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -510,6 +510,10 @@ int mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev, if (!dbidev->dbi.command) return -EINVAL;
+ ret = drm_mode_config_init(drm); + if (ret) + return ret; + dbidev->tx_buf = devm_kmalloc(drm->dev, tx_buf_size, GFP_KERNEL); if (!dbidev->tx_buf) return -ENOMEM; diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index 42bc5dadcb1c..c88b84366dc5 100644 --- a/drivers/gpu/drm/tiny/hx8357d.c +++ b/drivers/gpu/drm/tiny/hx8357d.c @@ -239,8 +239,6 @@ static int hx8357d_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm); - dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); if (IS_ERR(dc)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n"); diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index aae88dc5b3f7..fa998a16026c 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -390,8 +390,6 @@ static int ili9225_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm); - dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 7d40cb4ff72b..945e15169866 100644 --- a/drivers/gpu/drm/tiny/ili9341.c +++ b/drivers/gpu/drm/tiny/ili9341.c @@ -197,8 +197,6 @@ static int ili9341_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm); - dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 7d735fc67498..38d293cf5377 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -211,8 +211,6 @@ static int ili9486_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm); - dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c index 8555a56bce8c..b8c973bc2347 100644 --- a/drivers/gpu/drm/tiny/mi0283qt.c +++ b/drivers/gpu/drm/tiny/mi0283qt.c @@ -201,8 +201,6 @@ static int mi0283qt_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm); - dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 427c2561f5f4..1f1a576be93c 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -331,8 +331,6 @@ static int st7586_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm); - bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay;
dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index b447235c3d47..0f48a5a2d3d7 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -212,8 +212,6 @@ static int st7735r_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm); - dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
On Thu, Feb 27, 2020 at 07:15:18PM +0100, Daniel Vetter wrote:
7/7 drivers agree that's the right choice, let's do this.
This avoids duplicating the same old error checking code over all 7 drivers, which is the motivation here.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Tested-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maxime Ripard mripard@kernel.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Eric Anholt eric@anholt.net Cc: David Lechner david@lechnology.com Cc: Kamlesh Gurudasani kamlesh.gurudasani@gmail.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org
Acked-by: Sam Ravnborg sam@ravnborg.org
drivers/gpu/drm/drm_mipi_dbi.c | 4 ++++ drivers/gpu/drm/tiny/hx8357d.c | 2 -- drivers/gpu/drm/tiny/ili9225.c | 2 -- drivers/gpu/drm/tiny/ili9341.c | 2 -- drivers/gpu/drm/tiny/ili9486.c | 2 -- drivers/gpu/drm/tiny/mi0283qt.c | 2 -- drivers/gpu/drm/tiny/st7586.c | 2 -- drivers/gpu/drm/tiny/st7735r.c | 2 -- 8 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index a678e07508d4..9de1586659be 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -510,6 +510,10 @@ int mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev, if (!dbidev->dbi.command) return -EINVAL;
- ret = drm_mode_config_init(drm);
- if (ret)
return ret;
- dbidev->tx_buf = devm_kmalloc(drm->dev, tx_buf_size, GFP_KERNEL); if (!dbidev->tx_buf) return -ENOMEM;
diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index 42bc5dadcb1c..c88b84366dc5 100644 --- a/drivers/gpu/drm/tiny/hx8357d.c +++ b/drivers/gpu/drm/tiny/hx8357d.c @@ -239,8 +239,6 @@ static int hx8357d_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm);
- dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); if (IS_ERR(dc)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n");
diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index aae88dc5b3f7..fa998a16026c 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -390,8 +390,6 @@ static int ili9225_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm);
- dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 7d40cb4ff72b..945e15169866 100644 --- a/drivers/gpu/drm/tiny/ili9341.c +++ b/drivers/gpu/drm/tiny/ili9341.c @@ -197,8 +197,6 @@ static int ili9341_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm);
- dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 7d735fc67498..38d293cf5377 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -211,8 +211,6 @@ static int ili9486_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm);
- dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c index 8555a56bce8c..b8c973bc2347 100644 --- a/drivers/gpu/drm/tiny/mi0283qt.c +++ b/drivers/gpu/drm/tiny/mi0283qt.c @@ -201,8 +201,6 @@ static int mi0283qt_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm);
- dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 427c2561f5f4..1f1a576be93c 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -331,8 +331,6 @@ static int st7586_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
drm_mode_config_init(drm);
bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay;
dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index b447235c3d47..0f48a5a2d3d7 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -212,8 +212,6 @@ static int st7735r_probe(struct spi_device *spi) } drmm_add_final_kfree(drm, dbidev);
- drm_mode_config_init(drm);
- dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(dbi->reset)) { DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
-- 2.24.1
Allows us to drop the drm_driver.release callback from all drivers, and remove the mipi_dbi_release() function.
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Reviewed-by: Noralf Trønnes noralf@tronnes.org Tested-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maxime Ripard mripard@kernel.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Eric Anholt eric@anholt.net Cc: David Lechner david@lechnology.com Cc: Kamlesh Gurudasani kamlesh.gurudasani@gmail.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org --- drivers/gpu/drm/drm_mipi_dbi.c | 16 ---------------- drivers/gpu/drm/tiny/hx8357d.c | 1 - drivers/gpu/drm/tiny/ili9225.c | 1 - drivers/gpu/drm/tiny/ili9341.c | 1 - drivers/gpu/drm/tiny/ili9486.c | 1 - drivers/gpu/drm/tiny/mi0283qt.c | 1 - drivers/gpu/drm/tiny/st7586.c | 1 - drivers/gpu/drm/tiny/st7735r.c | 1 - include/drm/drm_mipi_dbi.h | 1 - 9 files changed, 24 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 9de1586659be..c0060a1c569f 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -582,22 +582,6 @@ int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, } EXPORT_SYMBOL(mipi_dbi_dev_init);
-/** - * mipi_dbi_release - DRM driver release helper - * @drm: DRM device - * - * This function finalizes and frees &mipi_dbi. - * - * Drivers can use this as their &drm_driver->release callback. - */ -void mipi_dbi_release(struct drm_device *drm) -{ - DRM_DEBUG_DRIVER("\n"); - - drm_mode_config_cleanup(drm); -} -EXPORT_SYMBOL(mipi_dbi_release); - /** * mipi_dbi_hw_reset - Hardware reset of controller * @dbi: MIPI DBI structure diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index c88b84366dc5..af7f3d10aac3 100644 --- a/drivers/gpu/drm/tiny/hx8357d.c +++ b/drivers/gpu/drm/tiny/hx8357d.c @@ -196,7 +196,6 @@ DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops); static struct drm_driver hx8357d_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &hx8357d_fops, - .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "hx8357d", diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index fa998a16026c..118477af4491 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -346,7 +346,6 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops); static struct drm_driver ili9225_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &ili9225_fops, - .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .name = "ili9225", .desc = "Ilitek ILI9225", diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 945e15169866..e152de369019 100644 --- a/drivers/gpu/drm/tiny/ili9341.c +++ b/drivers/gpu/drm/tiny/ili9341.c @@ -152,7 +152,6 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops); static struct drm_driver ili9341_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &ili9341_fops, - .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "ili9341", diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 38d293cf5377..577aea662aa4 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -165,7 +165,6 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9486_fops); static struct drm_driver ili9486_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &ili9486_fops, - .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "ili9486", diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c index b8c973bc2347..decaf57053ff 100644 --- a/drivers/gpu/drm/tiny/mi0283qt.c +++ b/drivers/gpu/drm/tiny/mi0283qt.c @@ -156,7 +156,6 @@ DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops); static struct drm_driver mi0283qt_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &mi0283qt_fops, - .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "mi0283qt", diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 1f1a576be93c..c3295c717ba6 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -285,7 +285,6 @@ DEFINE_DRM_GEM_CMA_FOPS(st7586_fops); static struct drm_driver st7586_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &st7586_fops, - .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7586", diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index 0f48a5a2d3d7..631801c36f46 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -157,7 +157,6 @@ DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops); static struct drm_driver st7735r_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &st7735r_fops, - .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7735r", diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h index 33f325f5af2b..4129bba55873 100644 --- a/include/drm/drm_mipi_dbi.h +++ b/include/drm/drm_mipi_dbi.h @@ -152,7 +152,6 @@ int mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev, int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, const struct drm_simple_display_pipe_funcs *funcs, const struct drm_display_mode *mode, unsigned int rotation); -void mipi_dbi_release(struct drm_device *drm); void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state); void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev,
On Thu, Feb 27, 2020 at 07:15:19PM +0100, Daniel Vetter wrote:
Allows us to drop the drm_driver.release callback from all drivers, and remove the mipi_dbi_release() function.
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Reviewed-by: Noralf Trønnes noralf@tronnes.org Tested-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maxime Ripard mripard@kernel.org Cc: Thomas Zimmermann tzimmermann@suse.de Cc: David Airlie airlied@linux.ie Cc: Daniel Vetter daniel@ffwll.ch Cc: Eric Anholt eric@anholt.net Cc: David Lechner david@lechnology.com Cc: Kamlesh Gurudasani kamlesh.gurudasani@gmail.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org
Acked-by: Sam Ravnborg sam@ravnborg.org
drivers/gpu/drm/drm_mipi_dbi.c | 16 ---------------- drivers/gpu/drm/tiny/hx8357d.c | 1 - drivers/gpu/drm/tiny/ili9225.c | 1 - drivers/gpu/drm/tiny/ili9341.c | 1 - drivers/gpu/drm/tiny/ili9486.c | 1 - drivers/gpu/drm/tiny/mi0283qt.c | 1 - drivers/gpu/drm/tiny/st7586.c | 1 - drivers/gpu/drm/tiny/st7735r.c | 1 - include/drm/drm_mipi_dbi.h | 1 - 9 files changed, 24 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 9de1586659be..c0060a1c569f 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -582,22 +582,6 @@ int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, } EXPORT_SYMBOL(mipi_dbi_dev_init);
-/**
- mipi_dbi_release - DRM driver release helper
- @drm: DRM device
- This function finalizes and frees &mipi_dbi.
- Drivers can use this as their &drm_driver->release callback.
- */
-void mipi_dbi_release(struct drm_device *drm) -{
- DRM_DEBUG_DRIVER("\n");
- drm_mode_config_cleanup(drm);
-} -EXPORT_SYMBOL(mipi_dbi_release);
/**
- mipi_dbi_hw_reset - Hardware reset of controller
- @dbi: MIPI DBI structure
diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index c88b84366dc5..af7f3d10aac3 100644 --- a/drivers/gpu/drm/tiny/hx8357d.c +++ b/drivers/gpu/drm/tiny/hx8357d.c @@ -196,7 +196,6 @@ DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops); static struct drm_driver hx8357d_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &hx8357d_fops,
- .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "hx8357d",
diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index fa998a16026c..118477af4491 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -346,7 +346,6 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops); static struct drm_driver ili9225_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &ili9225_fops,
- .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .name = "ili9225", .desc = "Ilitek ILI9225",
diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 945e15169866..e152de369019 100644 --- a/drivers/gpu/drm/tiny/ili9341.c +++ b/drivers/gpu/drm/tiny/ili9341.c @@ -152,7 +152,6 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops); static struct drm_driver ili9341_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &ili9341_fops,
- .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "ili9341",
diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 38d293cf5377..577aea662aa4 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -165,7 +165,6 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9486_fops); static struct drm_driver ili9486_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &ili9486_fops,
- .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "ili9486",
diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c index b8c973bc2347..decaf57053ff 100644 --- a/drivers/gpu/drm/tiny/mi0283qt.c +++ b/drivers/gpu/drm/tiny/mi0283qt.c @@ -156,7 +156,6 @@ DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops); static struct drm_driver mi0283qt_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &mi0283qt_fops,
- .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "mi0283qt",
diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 1f1a576be93c..c3295c717ba6 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -285,7 +285,6 @@ DEFINE_DRM_GEM_CMA_FOPS(st7586_fops); static struct drm_driver st7586_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &st7586_fops,
- .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7586",
diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index 0f48a5a2d3d7..631801c36f46 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -157,7 +157,6 @@ DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops); static struct drm_driver st7735r_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, .fops = &st7735r_fops,
- .release = mipi_dbi_release, DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7735r",
diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h index 33f325f5af2b..4129bba55873 100644 --- a/include/drm/drm_mipi_dbi.h +++ b/include/drm/drm_mipi_dbi.h @@ -152,7 +152,6 @@ int mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev, int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, const struct drm_simple_display_pipe_funcs *funcs, const struct drm_display_mode *mode, unsigned int rotation); -void mipi_dbi_release(struct drm_device *drm); void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state); void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, -- 2.24.1
It's right above the drm_dev_put().
This allows us to delete a bit of onion unwinding in udl_modeset_init().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Sam Ravnborg sam@ravnborg.org --- drivers/gpu/drm/udl/udl_drv.c | 1 - drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_modeset.c | 21 ++++++--------------- 3 files changed, 6 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 8b78c356beb5..b447fb053e78 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -37,7 +37,6 @@ DEFINE_DRM_GEM_FOPS(udl_driver_fops); static void udl_driver_release(struct drm_device *dev) { udl_fini(dev); - udl_modeset_cleanup(dev); }
static struct drm_driver driver = { diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index e67227c44cc4..1de7eb1b6aac 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -68,7 +68,6 @@ struct udl_device {
/* modeset */ int udl_modeset_init(struct drm_device *dev); -void udl_modeset_cleanup(struct drm_device *dev); struct drm_connector *udl_connector_init(struct drm_device *dev);
struct urb *udl_get_urb(struct drm_device *dev); diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index d59ebac70b15..cad0c87f8de6 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -468,7 +468,9 @@ int udl_modeset_init(struct drm_device *dev) struct drm_connector *connector; int ret;
- drm_mode_config_init(dev); + ret = drm_mode_config_init(dev); + if (ret) + return ret;
dev->mode_config.min_width = 640; dev->mode_config.min_height = 480; @@ -482,10 +484,8 @@ int udl_modeset_init(struct drm_device *dev) dev->mode_config.funcs = &udl_mode_funcs;
connector = udl_connector_init(dev); - if (IS_ERR(connector)) { - ret = PTR_ERR(connector); - goto err_drm_mode_config_cleanup; - } + if (IS_ERR(connector)) + return PTR_ERR(connector);
format_count = ARRAY_SIZE(udl_simple_display_pipe_formats);
@@ -494,18 +494,9 @@ int udl_modeset_init(struct drm_device *dev) udl_simple_display_pipe_formats, format_count, NULL, connector); if (ret) - goto err_drm_mode_config_cleanup; + return ret;
drm_mode_config_reset(dev);
return 0; - -err_drm_mode_config_cleanup: - drm_mode_config_cleanup(dev); - return ret; -} - -void udl_modeset_cleanup(struct drm_device *dev) -{ - drm_mode_config_cleanup(dev); }
On Thu, Feb 27, 2020 at 07:15:20PM +0100, Daniel Vetter wrote:
It's right above the drm_dev_put().
This allows us to delete a bit of onion unwinding in udl_modeset_init().
This is made possible by a preceeding patch which added a drmm_ cleanup action to drm_mode_config_init(), hence all we need to do to ensure that drm_mode_config_cleanup() is run on final drm_device cleanup is check the new error code for _init().
v2: Explain why this cleanup is possible (Laurent).
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Sam Ravnborg sam@ravnborg.org
Acked-by: Sam Ravnborg sam@ravnborg.org
drivers/gpu/drm/udl/udl_drv.c | 1 - drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_modeset.c | 21 ++++++--------------- 3 files changed, 6 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 8b78c356beb5..b447fb053e78 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -37,7 +37,6 @@ DEFINE_DRM_GEM_FOPS(udl_driver_fops); static void udl_driver_release(struct drm_device *dev) { udl_fini(dev);
- udl_modeset_cleanup(dev);
}
static struct drm_driver driver = { diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index e67227c44cc4..1de7eb1b6aac 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -68,7 +68,6 @@ struct udl_device {
/* modeset */ int udl_modeset_init(struct drm_device *dev); -void udl_modeset_cleanup(struct drm_device *dev); struct drm_connector *udl_connector_init(struct drm_device *dev);
struct urb *udl_get_urb(struct drm_device *dev); diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index d59ebac70b15..cad0c87f8de6 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -468,7 +468,9 @@ int udl_modeset_init(struct drm_device *dev) struct drm_connector *connector; int ret;
- drm_mode_config_init(dev);
ret = drm_mode_config_init(dev);
if (ret)
return ret;
dev->mode_config.min_width = 640; dev->mode_config.min_height = 480;
@@ -482,10 +484,8 @@ int udl_modeset_init(struct drm_device *dev) dev->mode_config.funcs = &udl_mode_funcs;
connector = udl_connector_init(dev);
- if (IS_ERR(connector)) {
ret = PTR_ERR(connector);
goto err_drm_mode_config_cleanup;
- }
if (IS_ERR(connector))
return PTR_ERR(connector);
format_count = ARRAY_SIZE(udl_simple_display_pipe_formats);
@@ -494,18 +494,9 @@ int udl_modeset_init(struct drm_device *dev) udl_simple_display_pipe_formats, format_count, NULL, connector); if (ret)
goto err_drm_mode_config_cleanup;
return ret;
drm_mode_config_reset(dev);
return 0;
-err_drm_mode_config_cleanup:
- drm_mode_config_cleanup(dev);
- return ret;
-}
-void udl_modeset_cleanup(struct drm_device *dev) -{
- drm_mode_config_cleanup(dev);
}
2.24.1
There's only two functions called from that: drm_kms_helper_poll_fini() and udl_free_urb_list(). Both of these are also called from the ubs_driver->disconnect hook, so entirely pointless to do the same again in the ->release hook.
Furthermore by the time we clean up the drm_driver we really shouldn't be touching hardware anymore, so stopping the poll worker and freeing the urb allocations in ->disconnect is the right thing to do.
Now disconnect still cleans things up before unregistering the driver, but that's a different issue.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/udl/udl_drv.c | 6 ------ drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_main.c | 10 ---------- 3 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b447fb053e78..7f140898df3e 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -34,14 +34,8 @@ static int udl_usb_resume(struct usb_interface *interface)
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
-static void udl_driver_release(struct drm_device *dev) -{ - udl_fini(dev); -} - static struct drm_driver driver = { .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, - .release = udl_driver_release,
/* gem hooks */ .gem_create_object = udl_driver_gem_create_object, diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1de7eb1b6aac..2642f94a63fc 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -76,7 +76,6 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb);
int udl_init(struct udl_device *udl); -void udl_fini(struct drm_device *dev);
int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 538718919916..f5d27f2a5654 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -351,13 +351,3 @@ int udl_drop_usb(struct drm_device *dev) udl_free_urb_list(dev); return 0; } - -void udl_fini(struct drm_device *dev) -{ - struct udl_device *udl = to_udl(dev); - - drm_kms_helper_poll_fini(dev); - - if (udl->urbs.count) - udl_free_urb_list(dev); -}
Hi Daniel
Am 27.02.20 um 19:15 schrieb Daniel Vetter:
There's only two functions called from that: drm_kms_helper_poll_fini() and udl_free_urb_list(). Both of these are also called from the ubs_driver->disconnect hook, so entirely pointless to do the same again in the ->release hook.
The disconnect hook calls drm_kms_helper_poll_disable() instead if _fini(). They are the same, except that _disable() does not clear dev->mode_config.poll_enabled to false. Is this OK?
Best regards Thomas
Furthermore by the time we clean up the drm_driver we really shouldn't be touching hardware anymore, so stopping the poll worker and freeing the urb allocations in ->disconnect is the right thing to do.
Now disconnect still cleans things up before unregistering the driver, but that's a different issue.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/udl/udl_drv.c | 6 ------ drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_main.c | 10 ---------- 3 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b447fb053e78..7f140898df3e 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -34,14 +34,8 @@ static int udl_usb_resume(struct usb_interface *interface)
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
-static void udl_driver_release(struct drm_device *dev) -{
- udl_fini(dev);
-}
static struct drm_driver driver = { .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.release = udl_driver_release,
/* gem hooks */ .gem_create_object = udl_driver_gem_create_object,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1de7eb1b6aac..2642f94a63fc 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -76,7 +76,6 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb);
int udl_init(struct udl_device *udl); -void udl_fini(struct drm_device *dev);
int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 538718919916..f5d27f2a5654 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -351,13 +351,3 @@ int udl_drop_usb(struct drm_device *dev) udl_free_urb_list(dev); return 0; }
-void udl_fini(struct drm_device *dev) -{
- struct udl_device *udl = to_udl(dev);
- drm_kms_helper_poll_fini(dev);
- if (udl->urbs.count)
udl_free_urb_list(dev);
-}
On Fri, Feb 28, 2020 at 8:44 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 27.02.20 um 19:15 schrieb Daniel Vetter:
There's only two functions called from that: drm_kms_helper_poll_fini() and udl_free_urb_list(). Both of these are also called from the ubs_driver->disconnect hook, so entirely pointless to do the same again in the ->release hook.
The disconnect hook calls drm_kms_helper_poll_disable() instead if _fini(). They are the same, except that _disable() does not clear dev->mode_config.poll_enabled to false. Is this OK?
oops, I overlooked that. But yeah for driver shutdown it's the same really, we're not going to re-enable. _disable is meant for suspend so youc an re-enable again on resume.
I'll augment the commit message on the next round to clarify that. -Daniel
Best regards Thomas
Furthermore by the time we clean up the drm_driver we really shouldn't be touching hardware anymore, so stopping the poll worker and freeing the urb allocations in ->disconnect is the right thing to do.
Now disconnect still cleans things up before unregistering the driver, but that's a different issue.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/udl/udl_drv.c | 6 ------ drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_main.c | 10 ---------- 3 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b447fb053e78..7f140898df3e 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -34,14 +34,8 @@ static int udl_usb_resume(struct usb_interface *interface)
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
-static void udl_driver_release(struct drm_device *dev) -{
udl_fini(dev);
-}
static struct drm_driver driver = { .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.release = udl_driver_release, /* gem hooks */ .gem_create_object = udl_driver_gem_create_object,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1de7eb1b6aac..2642f94a63fc 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -76,7 +76,6 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb);
int udl_init(struct udl_device *udl); -void udl_fini(struct drm_device *dev);
int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 538718919916..f5d27f2a5654 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -351,13 +351,3 @@ int udl_drop_usb(struct drm_device *dev) udl_free_urb_list(dev); return 0; }
-void udl_fini(struct drm_device *dev) -{
struct udl_device *udl = to_udl(dev);
drm_kms_helper_poll_fini(dev);
if (udl->urbs.count)
udl_free_urb_list(dev);
-}
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
Hi
Am 28.02.20 um 09:40 schrieb Daniel Vetter:
On Fri, Feb 28, 2020 at 8:44 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 27.02.20 um 19:15 schrieb Daniel Vetter:
There's only two functions called from that: drm_kms_helper_poll_fini() and udl_free_urb_list(). Both of these are also called from the ubs_driver->disconnect hook, so entirely pointless to do the same again in the ->release hook.
The disconnect hook calls drm_kms_helper_poll_disable() instead if _fini(). They are the same, except that _disable() does not clear dev->mode_config.poll_enabled to false. Is this OK?
oops, I overlooked that. But yeah for driver shutdown it's the same really, we're not going to re-enable. _disable is meant for suspend so youc an re-enable again on resume.
I'll augment the commit message on the next round to clarify that.
Well, we have a managed API. It could support drmm_kms_helper_poll_init(). :)
Best regards Thomas
-Daniel
Best regards Thomas
Furthermore by the time we clean up the drm_driver we really shouldn't be touching hardware anymore, so stopping the poll worker and freeing the urb allocations in ->disconnect is the right thing to do.
Now disconnect still cleans things up before unregistering the driver, but that's a different issue.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/udl/udl_drv.c | 6 ------ drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_main.c | 10 ---------- 3 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b447fb053e78..7f140898df3e 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -34,14 +34,8 @@ static int udl_usb_resume(struct usb_interface *interface)
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
-static void udl_driver_release(struct drm_device *dev) -{
udl_fini(dev);
-}
static struct drm_driver driver = { .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.release = udl_driver_release, /* gem hooks */ .gem_create_object = udl_driver_gem_create_object,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1de7eb1b6aac..2642f94a63fc 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -76,7 +76,6 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb);
int udl_init(struct udl_device *udl); -void udl_fini(struct drm_device *dev);
int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 538718919916..f5d27f2a5654 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -351,13 +351,3 @@ int udl_drop_usb(struct drm_device *dev) udl_free_urb_list(dev); return 0; }
-void udl_fini(struct drm_device *dev) -{
struct udl_device *udl = to_udl(dev);
drm_kms_helper_poll_fini(dev);
if (udl->urbs.count)
udl_free_urb_list(dev);
-}
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
On Fri, Feb 28, 2020 at 12:46 PM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi
Am 28.02.20 um 09:40 schrieb Daniel Vetter:
On Fri, Feb 28, 2020 at 8:44 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 27.02.20 um 19:15 schrieb Daniel Vetter:
There's only two functions called from that: drm_kms_helper_poll_fini() and udl_free_urb_list(). Both of these are also called from the ubs_driver->disconnect hook, so entirely pointless to do the same again in the ->release hook.
The disconnect hook calls drm_kms_helper_poll_disable() instead if _fini(). They are the same, except that _disable() does not clear dev->mode_config.poll_enabled to false. Is this OK?
oops, I overlooked that. But yeah for driver shutdown it's the same really, we're not going to re-enable. _disable is meant for suspend so youc an re-enable again on resume.
I'll augment the commit message on the next round to clarify that.
Well, we have a managed API. It could support drmm_kms_helper_poll_init(). :)
You're ahead of the game here, but yes that's the plan. And a lot more. Ideally I really want to get rid of both bus_driver->remove and drm_driver->release callbacks for all drivers.
Also, for polling you actually want devm_kms_poll_init, since polling should be stopped at unplug/remove time. Not at drm_driver release time :-) -Daniel
Best regards Thomas
-Daniel
Best regards Thomas
Furthermore by the time we clean up the drm_driver we really shouldn't be touching hardware anymore, so stopping the poll worker and freeing the urb allocations in ->disconnect is the right thing to do.
Now disconnect still cleans things up before unregistering the driver, but that's a different issue.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/udl/udl_drv.c | 6 ------ drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_main.c | 10 ---------- 3 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b447fb053e78..7f140898df3e 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -34,14 +34,8 @@ static int udl_usb_resume(struct usb_interface *interface)
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
-static void udl_driver_release(struct drm_device *dev) -{
udl_fini(dev);
-}
static struct drm_driver driver = { .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.release = udl_driver_release, /* gem hooks */ .gem_create_object = udl_driver_gem_create_object,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1de7eb1b6aac..2642f94a63fc 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -76,7 +76,6 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb);
int udl_init(struct udl_device *udl); -void udl_fini(struct drm_device *dev);
int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 538718919916..f5d27f2a5654 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -351,13 +351,3 @@ int udl_drop_usb(struct drm_device *dev) udl_free_urb_list(dev); return 0; }
-void udl_fini(struct drm_device *dev) -{
struct udl_device *udl = to_udl(dev);
drm_kms_helper_poll_fini(dev);
if (udl->urbs.count)
udl_free_urb_list(dev);
-}
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
Hi Daniel
Am 28.02.20 um 18:43 schrieb Daniel Vetter:
On Fri, Feb 28, 2020 at 12:46 PM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi
Am 28.02.20 um 09:40 schrieb Daniel Vetter:
On Fri, Feb 28, 2020 at 8:44 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 27.02.20 um 19:15 schrieb Daniel Vetter:
There's only two functions called from that: drm_kms_helper_poll_fini() and udl_free_urb_list(). Both of these are also called from the ubs_driver->disconnect hook, so entirely pointless to do the same again in the ->release hook.
The disconnect hook calls drm_kms_helper_poll_disable() instead if _fini(). They are the same, except that _disable() does not clear dev->mode_config.poll_enabled to false. Is this OK?
oops, I overlooked that. But yeah for driver shutdown it's the same really, we're not going to re-enable. _disable is meant for suspend so youc an re-enable again on resume.
I'll augment the commit message on the next round to clarify that.
Well, we have a managed API. It could support drmm_kms_helper_poll_init(). :)
You're ahead of the game here, but yes that's the plan. And a lot more. Ideally I really want to get rid of both bus_driver->remove and drm_driver->release callbacks for all drivers.
Also, for polling you actually want devm_kms_poll_init, since polling should be stopped at unplug/remove time. Not at drm_driver release time :-)
Quite honestly, if you're not adding devm_kms_poll_init() now, why even bother removing the _fini() call from release()? It hasn't been a problem so far and it won't become one. Doing a half-baked change now results in a potential WTF moment for other developers.
Rather clean up release() when you add the managed devm_kms_poll_init() and have a nice, clear patch.
Best regards Thomas
-Daniel
Best regards Thomas
-Daniel
Best regards Thomas
Furthermore by the time we clean up the drm_driver we really shouldn't be touching hardware anymore, so stopping the poll worker and freeing the urb allocations in ->disconnect is the right thing to do.
Now disconnect still cleans things up before unregistering the driver, but that's a different issue.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/udl/udl_drv.c | 6 ------ drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_main.c | 10 ---------- 3 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b447fb053e78..7f140898df3e 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -34,14 +34,8 @@ static int udl_usb_resume(struct usb_interface *interface)
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
-static void udl_driver_release(struct drm_device *dev) -{
udl_fini(dev);
-}
static struct drm_driver driver = { .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.release = udl_driver_release, /* gem hooks */ .gem_create_object = udl_driver_gem_create_object,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1de7eb1b6aac..2642f94a63fc 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -76,7 +76,6 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb);
int udl_init(struct udl_device *udl); -void udl_fini(struct drm_device *dev);
int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 538718919916..f5d27f2a5654 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -351,13 +351,3 @@ int udl_drop_usb(struct drm_device *dev) udl_free_urb_list(dev); return 0; }
-void udl_fini(struct drm_device *dev) -{
struct udl_device *udl = to_udl(dev);
drm_kms_helper_poll_fini(dev);
if (udl->urbs.count)
udl_free_urb_list(dev);
-}
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
On Mon, Mar 2, 2020 at 9:14 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 28.02.20 um 18:43 schrieb Daniel Vetter:
On Fri, Feb 28, 2020 at 12:46 PM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi
Am 28.02.20 um 09:40 schrieb Daniel Vetter:
On Fri, Feb 28, 2020 at 8:44 AM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 27.02.20 um 19:15 schrieb Daniel Vetter:
There's only two functions called from that: drm_kms_helper_poll_fini() and udl_free_urb_list(). Both of these are also called from the ubs_driver->disconnect hook, so entirely pointless to do the same again in the ->release hook.
The disconnect hook calls drm_kms_helper_poll_disable() instead if _fini(). They are the same, except that _disable() does not clear dev->mode_config.poll_enabled to false. Is this OK?
oops, I overlooked that. But yeah for driver shutdown it's the same really, we're not going to re-enable. _disable is meant for suspend so youc an re-enable again on resume.
I'll augment the commit message on the next round to clarify that.
Well, we have a managed API. It could support drmm_kms_helper_poll_init(). :)
You're ahead of the game here, but yes that's the plan. And a lot more. Ideally I really want to get rid of both bus_driver->remove and drm_driver->release callbacks for all drivers.
Also, for polling you actually want devm_kms_poll_init, since polling should be stopped at unplug/remove time. Not at drm_driver release time :-)
Quite honestly, if you're not adding devm_kms_poll_init() now, why even bother removing the _fini() call from release()? It hasn't been a problem so far and it won't become one. Doing a half-baked change now results in a potential WTF moment for other developers.
Rather clean up release() when you add the managed devm_kms_poll_init() and have a nice, clear patch.
So the thing is, you need to stop your poll worker in ->unplug, not ->release. That's the part I'm fixing here (plus stopping it twice is overkill). I'm not seeing any connection to making the ->unplug part here automatic, that was just a future idea. But this patch series here is all about ->release stuff. I guess I can do a respin and change the one in ->unplug from _disable to _fini, which would be clearer.
But not doing this patch here just because we want to clean things up even more doesn't make sense to me. -Daniel
Best regards Thomas
-Daniel
Best regards Thomas
-Daniel
Best regards Thomas
Furthermore by the time we clean up the drm_driver we really shouldn't be touching hardware anymore, so stopping the poll worker and freeing the urb allocations in ->disconnect is the right thing to do.
Now disconnect still cleans things up before unregistering the driver, but that's a different issue.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com Cc: Dave Airlie airlied@redhat.com Cc: Sean Paul sean@poorly.run Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Thomas Zimmermann tzimmermann@suse.de Cc: Emil Velikov emil.l.velikov@gmail.com Cc: Gerd Hoffmann kraxel@redhat.com Cc: "Noralf Trønnes" noralf@tronnes.org Cc: Sam Ravnborg sam@ravnborg.org Cc: Thomas Gleixner tglx@linutronix.de Cc: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/udl/udl_drv.c | 6 ------ drivers/gpu/drm/udl/udl_drv.h | 1 - drivers/gpu/drm/udl/udl_main.c | 10 ---------- 3 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b447fb053e78..7f140898df3e 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -34,14 +34,8 @@ static int udl_usb_resume(struct usb_interface *interface)
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
-static void udl_driver_release(struct drm_device *dev) -{
udl_fini(dev);
-}
static struct drm_driver driver = { .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.release = udl_driver_release, /* gem hooks */ .gem_create_object = udl_driver_gem_create_object,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 1de7eb1b6aac..2642f94a63fc 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -76,7 +76,6 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb);
int udl_init(struct udl_device *udl); -void udl_fini(struct drm_device *dev);
int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 538718919916..f5d27f2a5654 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -351,13 +351,3 @@ int udl_drop_usb(struct drm_device *dev) udl_free_urb_list(dev); return 0; }
-void udl_fini(struct drm_device *dev) -{
struct udl_device *udl = to_udl(dev);
drm_kms_helper_poll_fini(dev);
if (udl->urbs.count)
udl_free_urb_list(dev);
-}
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
All collected together to provide a consistent story in one patch, instead of the somewhat bumpy refactor-evolution leading to this.
Also some thoughts on what the next steps could be:
- Create a macro called devm_drm_dev_alloc() which essentially wraps the kzalloc(); devm_drm_dev_init(); drmm_add_final_kfree() combo. Needs to be a macro since we'll have to do some typeof trickery and casting to make this fully generic for all drivers that embed struct drm_device into their own thing.
- A lot of the simple drivers now have essentially just drm_dev_unplug(); drm_atomic_helper_shutdown(); as their $bus_driver->remove hook. We could create a devm_mode_config_reset which sets drm_atomic_helper_shutdown as it's cleanup action, and a devm_drm_dev_register with drm_dev_unplug as it's cleanup action, and simple drivers wouldn't have a need for a ->remove function at all, and we could delete them.
- For more complicated drivers we need drmm_ versions of a _lot_ more things. All the userspace visible objects (crtc, plane, encoder, crtc), anything else hanging of those (maybe a drmm_get_edid, at least for panels and other built-in stuff).
Also some more thoughts on why we're not reusing devm_ with maybe a fake struct device embedded into the drm_device (we can't use the kdev, since that's in each drm_minor).
- Code review gets extremely tricky, since every time you see a devm_ you need to carefully check whether the fake device (with the drm_device lifetim) or the real device (with the lifetim of the underlying physical device and driver binding) are used. That's not going to help at all, and we have enormous amounts of drivers who use devm_ where they really shouldn't. Having different types makes sure the compiler type checks this for us and ensures correctness.
- The set of functions are very much non-overlapping. E.g. devm_ioremap makes total sense, drmm_ioremap has the wrong lifetime, since hw resources need to be cleaned out at driver unbind and wont outlive that like a drm_device. Similar, but other way round for drmm_connector_init (which is the only correct version, devm_ for drm_connector is just buggy). Simply not having the wrong version again prevents bugs.
Finally I guess this opens a huge todo for all the drivers. I'm semi-tempted to do a tree-wide s/devm_kzalloc/drmm_kzalloc/ since most likely that'll fix an enormous amount of bugs and most likely not cause any issues at all (aside from maybe holding onto memory slightly too long).
v2: - Doc improvements from Laurent. - Also add kerneldoc for the new drmm_add_action_or_reset.
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: "Rafael J. Wysocki" rafael@kernel.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- Documentation/gpu/drm-internals.rst | 6 +++ drivers/gpu/drm/drm_drv.c | 18 +++++++-- drivers/gpu/drm/drm_managed.c | 61 +++++++++++++++++++++++++++++ include/drm/drm_drv.h | 4 ++ include/drm/drm_managed.h | 58 +++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 3 deletions(-)
diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index a6b6145fda78..12272b168580 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -138,6 +138,12 @@ Managed Resources .. kernel-doc:: drivers/gpu/drm/drm_managed.c :doc: managed resources
+.. kernel-doc:: drivers/gpu/drm/drm_managed.c + :export: + +.. kernel-doc:: include/drm/drm_managed.h + :internal: + Bus-specific Device Registration and PCI Support ------------------------------------------------
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index bb326b9bcde0..f86b2bae0fa0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -260,9 +260,15 @@ void drm_minor_release(struct drm_minor *minor) * any other resources allocated at device initialization and drop the driver's * reference to &drm_device using drm_dev_put(). * - * Note that the lifetime rules for &drm_device instance has still a lot of - * historical baggage. Hence use the reference counting provided by - * drm_dev_get() and drm_dev_put() only carefully. + * Note that any allocation or resource which is visible to userspace must be + * released only when the final drm_dev_put() is called, and not when the + * driver is unbound from the underlying physical struct &device. Best to use + * &drm_device managed resources with drmm_add_action(), drmm_kmalloc() and + * related functions. + * + * devres managed resources like devm_kmalloc() can only be used for resources + * directly related to the underlying hardware device, and only used in code + * paths fully protected by drm_dev_enter() and drm_dev_exit(). * * Display driver example * ~~~~~~~~~~~~~~~~~~~~~~ @@ -606,6 +612,9 @@ static void drm_dev_init_release(struct drm_device *dev, void *res) * arbitrary offset, you must supply a &drm_driver.release callback and control * the finalization explicitly. * + * Note that drivers must call drmm_add_final_kfree() after this function has + * completed successfully. + * * RETURNS: * 0 on success, or error code on failure. */ @@ -707,6 +716,9 @@ static void devm_drm_dev_init_release(void *data) * Managed drm_dev_init(). The DRM device initialized with this function is * automatically put on driver detach using drm_dev_put(). * + * Note that drivers must call drmm_add_final_kfree() after this function has + * completed successfully. + * * RETURNS: * 0 on success, or error code on failure. */ diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c index 6376be01bbc8..f040efea174e 100644 --- a/drivers/gpu/drm/drm_managed.c +++ b/drivers/gpu/drm/drm_managed.c @@ -20,7 +20,19 @@ * Inspired by struct &device managed resources, but tied to the lifetime of * struct &drm_device, which can outlive the underlying physical device, usually * when userspace has some open files and other handles to resources still open. + * + * Release actions can be added with drmm_add_action(), memory allocations can + * be done directly with drmm_kmalloc() and the related functions. Everything + * will be released on the final drm_dev_put() in reverse order of how the + * release actions have been added and memory has been allocated since driver + * loading started with drm_dev_init(). + * + * Note that release actions and managed memory can also be added and removed + * during the lifetime of the driver, all the functions are fully concurrent + * safe. But it is recommended to use managed resources only for resources that + * change rarely, if ever, during the lifetime of the &drm_device instance. */ + struct drmres_node { struct list_head entry; drmres_release_t release; @@ -101,6 +113,18 @@ static void add_dr(struct drm_device *dev, struct drmres *dr) dr, dr->node.name, (unsigned long) dr->node.size); }
+/** + * drmm_add_final_kfree - add release action for the final kfree() + * @dev: DRM device + * @parent: pointer to the kmalloc allocation containing @dev + * + * Since the allocation containing the struct &drm_device must be allocated + * before it can be initialized with drm_dev_init() there's no way to allocate + * that memory with drmm_kmalloc(). To side-step this chicken-egg problem the + * pointer for this final kfree() must be specified by calling this function. It + * will be released in the final drm_dev_put() for @dev, after all other release + * actions installed through drmm_add_action() have been processed. + */ void drmm_add_final_kfree(struct drm_device *dev, void *parent) { WARN_ON(dev->managed.final_kfree); @@ -148,6 +172,14 @@ int __drmm_add_action_or_reset(struct drm_device *dev, } EXPORT_SYMBOL(__drmm_add_action_or_reset);
+/** + * drmm_add_action - remove a managed release action to a &drm_device + * @dev: DRM device + * @action: release function + * @data: opaque pointer, passed to @action + * + * This function removes a release action added by drmm_add_action(). + */ void drmm_remove_action(struct drm_device *dev, drmres_release_t action, void *data) @@ -176,6 +208,16 @@ void drmm_remove_action(struct drm_device *dev, } EXPORT_SYMBOL(drmm_remove_action);
+/** + * drmm_kmalloc - &drm_device managed kmalloc() + * @dev: DRM device + * @size: size of the memory allocation + * @gfp: GFP allocation flags + * + * This is a &drm_device managed version of kmalloc(). The allocated memory is + * automatically freed on the final drm_dev_put(). Memory can also be freed + * before the final drm_dev_put() by calling drmm_kfree(). + */ void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) { struct drmres *dr; @@ -191,6 +233,16 @@ void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) } EXPORT_SYMBOL(drmm_kmalloc);
+/** + * drmm_kstrdup - &drm_device managed kstrdup() + * @dev: DRM device + * @s: 0-terminated string to be duplicated + * @gfp: GFP allocation flags + * + * This is a &drm_device managed version of kstrdup(). The allocated memory is + * automatically freed on the final drm_dev_put() and works exactly like a + * memory allocation obtained by drmm_kmalloc(). + */ char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp) { size_t size; @@ -207,6 +259,15 @@ char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp) } EXPORT_SYMBOL_GPL(drmm_kstrdup);
+/** + * drmm_kfree - &drm_device managed kfree() + * @dev: DRM device + * @data: memory allocation to be freed + * + * This is a &drm_device managed version of kfree() which can be used to + * release memory allocated through drmm_kmalloc() or any of its related + * functions before the final drm_dev_put() of @dev. + */ void drmm_kfree(struct drm_device *dev, void *data) { struct drmres *dr_match = NULL, *dr; diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index edee40e31e4b..0fd7fc6f024e 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -266,6 +266,10 @@ struct drm_driver { * * Optional callback for destroying device data after the final * reference is released, i.e. the device is being destroyed. + * + * This is deprecated, clean up all memory allocations associated with a + * &drm_device using drmm_add_action(), drmm_kmalloc() and related + * managed resources functions. */ void (*release) (struct drm_device *);
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 1e6291407586..af152cfb173c 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -11,6 +11,19 @@ struct drm_device;
typedef void (*drmres_release_t)(struct drm_device *dev, void *res);
+/** + * drmm_add_action - add a managed release action to a &drm_device + * @dev: DRM device + * @action: function which should be called when @dev is released + * @data: opaque pointer, passed to @action + * + * This function adds the @release action with optional parameter @data to the + * list of cleanup actions for @dev. The cleanup actions will be run in reverse + * order in the final drm_dev_put() call for @dev. + * + * A release action can be removed before @dev is released by calling + * drmm_remove_action() with matching parameters for @action and @data. + */ #define drmm_add_action(dev, action, data) \ __drmm_add_action(dev, action, data, #action)
@@ -18,6 +31,15 @@ int __must_check __drmm_add_action(struct drm_device *dev, drmres_release_t action, void *data, const char *name);
+/** + * drmm_add_action_or_reset - add a managed release action to a &drm_device + * @dev: DRM device + * @action: function which should be called when @dev is released + * @data: opaque pointer, passed to @action + * + * Similar to drmm_add_action(), with the only difference that upon failure + * @action is directly called for any cleanup work necessary on failures. + */ #define drmm_add_action_or_reset(dev, action, data) \ __drmm_add_action_or_reset(dev, action, data, #action)
@@ -32,10 +54,33 @@ void drmm_remove_action(struct drm_device *dev, void drmm_add_final_kfree(struct drm_device *dev, void *parent);
void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc; + +/** + * drmm_kzalloc - &drm_device managed kzalloc() + * @dev: DRM device + * @size: size of the memory allocation + * @gfp: GFP allocation flags + * + * This is a &drm_device managed version of kzalloc(). The allocated memory is + * automatically freed on the final drm_dev_put(). Memory can also be freed + * before the final drm_dev_put() by calling drmm_kfree(). + */ static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp) { return drmm_kmalloc(dev, size, gfp | __GFP_ZERO); } + +/** + * drmm_kmalloc_array - &drm_device managed kmalloc_array() + * @dev: DRM device + * @n: number of array elements to allocate + * @size: size of array member + * @flags: GFP allocation flags + * + * This is a &drm_device managed version of kmalloc_array(). The allocated + * memory is automatically freed on the final drm_dev_put() and works exactly + * like a memory allocation obtained by drmm_kmalloc(). + */ static inline void *drmm_kmalloc_array(struct drm_device *dev, size_t n, size_t size, gfp_t flags) { @@ -46,11 +91,24 @@ static inline void *drmm_kmalloc_array(struct drm_device *dev,
return drmm_kmalloc(dev, bytes, flags); } + +/** + * drmm_kcalloc - &drm_device managed kcalloc() + * @dev: DRM device + * @n: number of array elements to allocate + * @size: size of array member + * @flags: GFP allocation flags + * + * This is a &drm_device managed version of kcalloc(). The allocated memory is + * automatically freed on the final drm_dev_put() and works exactly like a + * memory allocation obtained by drmm_kmalloc(). + */ static inline void *drmm_kcalloc(struct drm_device *dev, size_t n, size_t size, gfp_t flags) { return drmm_kmalloc_array(dev, n, size, flags | __GFP_ZERO); } + char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp);
void drmm_kfree(struct drm_device *dev, void *data);
Hi Daniel.
Nicely written overview.
Acked-by: Sam Ravnborg sam@ravnborg.org
snip - detailed changelog + patch
dri-devel@lists.freedesktop.org