From 15f389da11257b806da75a070cfa41ca0cc15aae Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 9 Aug 2023 16:56:41 +0200 Subject: drm: bridge: samsung-dsim: Fix waiting for empty cmd transfer FIFO on older Exynos Samsung DSIM used in older Exynos SoCs (like Exynos 4210, 4x12, 3250) doesn't report empty level of packer header FIFO. In case of those SoCs, use the old way of waiting for empty command tranfsfer FIFO, removed recently by commit 14806c641582 ("drm: bridge: samsung-dsim: Drain command transfer FIFO before transfer"). Fixes: 14806c641582 ("drm: bridge: samsung-dsim: Drain command transfer FIFO before transfer") Signed-off-by: Marek Szyprowski Reviewed-by: Marek Vasut Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20230809145641.3213210-1-m.szyprowski@samsung.com --- include/drm/bridge/samsung-dsim.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/drm') diff --git a/include/drm/bridge/samsung-dsim.h b/include/drm/bridge/samsung-dsim.h index 05100e91ecb9..6fc9bb2979e4 100644 --- a/include/drm/bridge/samsung-dsim.h +++ b/include/drm/bridge/samsung-dsim.h @@ -53,6 +53,7 @@ struct samsung_dsim_driver_data { unsigned int plltmr_reg; unsigned int has_freqband:1; unsigned int has_clklane_stop:1; + unsigned int has_broken_fifoctrl_emptyhdr:1; unsigned int num_clks; unsigned int min_freq; unsigned int max_freq; -- cgit From ad1367f831f8743746a1f49705c28e36a7c95525 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 17 Aug 2023 09:48:09 -0700 Subject: drm/bridge: Fix kernel-doc typo in desc of output_bus_cfg in drm_bridge_state There's an obvious copy-paste error in the description of output_bus_cfg. Fix it. Fixes: f32df58acc68 ("drm/bridge: Add the necessary bits to support bus format negotiation") Signed-off-by: Douglas Anderson Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20230817094808.1.I41b04c3a8305c9f1c17af886c327941c5136ca3b@changeid --- include/drm/drm_atomic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 9a022caacf93..cf8e1220a4ac 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -1126,7 +1126,7 @@ struct drm_bridge_state { struct drm_bus_cfg input_bus_cfg; /** - * @output_bus_cfg: input bus configuration + * @output_bus_cfg: output bus configuration */ struct drm_bus_cfg output_bus_cfg; }; -- cgit From 2e3c369f23a77c404fd6b364a120a546f30e651c Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 21 Aug 2023 11:06:27 -0700 Subject: drm/i915/mtl: Eliminate subplatforms Now that we properly match the Xe_LPG IP versions associated with various workarounds, there's no longer any need to define separate MTL subplatform in the driver. Nothing in the code is conditional on MTL-M or MTL-P base platforms. Furthermore, I'm not sure the "M" and "P" designations are even an accurate representation of which specific platforms would have which IP versions; those were mostly just placeholders from a long time ago. The reality is that the IP version present on a platform gets read from a fuse register at driver init; we shouldn't be trying to guess which IP is present based on PCI ID anymore. Signed-off-by: Matt Roper Reviewed-by: Nemesa Garg Reviewed-by: Gustavo Sousa Link: https://patchwork.freedesktop.org/patch/msgid/20230821180619.650007-18-matthew.d.roper@intel.com --- include/drm/i915_pciids.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'include/drm') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index e1e10dfbb661..38dae757d1a8 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -738,18 +738,13 @@ #define INTEL_ATS_M_IDS(info) \ INTEL_ATS_M150_IDS(info), \ INTEL_ATS_M75_IDS(info) + /* MTL */ -#define INTEL_MTL_M_IDS(info) \ +#define INTEL_MTL_IDS(info) \ INTEL_VGA_DEVICE(0x7D40, info), \ - INTEL_VGA_DEVICE(0x7D60, info) - -#define INTEL_MTL_P_IDS(info) \ INTEL_VGA_DEVICE(0x7D45, info), \ INTEL_VGA_DEVICE(0x7D55, info), \ + INTEL_VGA_DEVICE(0x7D60, info), \ INTEL_VGA_DEVICE(0x7DD5, info) -#define INTEL_MTL_IDS(info) \ - INTEL_MTL_M_IDS(info), \ - INTEL_MTL_P_IDS(info) - #endif /* _I915_PCIIDS_H */ -- cgit From 8940da9fe5f278ac6ecb4cafa55c784f524cb3b2 Mon Sep 17 00:00:00 2001 From: Nemesa Garg Date: Tue, 22 Aug 2023 22:57:43 +0530 Subject: drm/i915/mtl: Adding DeviceID for Arrowlake-S under MTL Arrowlake-S graphics is similar enough to Meteorlake that we can treat them as the same platform Signed-off-by: Nemesa Garg Reviewed-by: Matt Roper Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20230822172743.2113377-1-nemesa.garg@intel.com --- include/drm/i915_pciids.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/drm') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 38dae757d1a8..1661f9e552d2 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -745,6 +745,7 @@ INTEL_VGA_DEVICE(0x7D45, info), \ INTEL_VGA_DEVICE(0x7D55, info), \ INTEL_VGA_DEVICE(0x7D60, info), \ + INTEL_VGA_DEVICE(0x7D67, info), \ INTEL_VGA_DEVICE(0x7DD5, info) #endif /* _I915_PCIIDS_H */ -- cgit From 0b30d57acafcaa5374756d314ee54f80d0bcc860 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 29 Aug 2023 13:01:13 +0200 Subject: drm/debugfs: rework debugfs directory creation v5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of the per minor directories only create a single debugfs directory for the whole device directly when the device is initialized. For DRM devices each minor gets a symlink to the per device directory for now until we can be sure that this isn't useful any more in any way. Accel devices create only the per device directory and also drops the mid layer callback to create driver specific files. v2: cleanup accel component as well v3: fix typo when debugfs is disabled v4: call drm_debugfs_dev_fini() during release as well, some kerneldoc typos fixed v5: rebased and one more kerneldoc fix Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20230829110115.3442-4-christian.koenig@amd.com Reviewed-by: Andi Shyti --- include/drm/drm_accel.h | 9 +++++++-- include/drm/drm_bridge.h | 2 +- include/drm/drm_client.h | 2 +- include/drm/drm_device.h | 7 +++++++ include/drm/drm_drv.h | 8 ++++++++ include/drm/drm_file.h | 1 + 6 files changed, 25 insertions(+), 4 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_accel.h b/include/drm/drm_accel.h index d4955062c77e..f4d3784b1dce 100644 --- a/include/drm/drm_accel.h +++ b/include/drm/drm_accel.h @@ -58,7 +58,8 @@ int accel_minor_alloc(void); void accel_minor_replace(struct drm_minor *minor, int index); void accel_set_device_instance_params(struct device *kdev, int index); int accel_open(struct inode *inode, struct file *filp); -void accel_debugfs_init(struct drm_minor *minor, int minor_id); +void accel_debugfs_init(struct drm_device *dev); +void accel_debugfs_register(struct drm_device *dev); #else @@ -89,7 +90,11 @@ static inline void accel_set_device_instance_params(struct device *kdev, int ind { } -static inline void accel_debugfs_init(struct drm_minor *minor, int minor_id) +static inline void accel_debugfs_init(struct drm_device *dev) +{ +} + +static inline void accel_debugfs_register(struct drm_device *dev) { } diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index c339fc85fd07..2dd94224f17e 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -950,6 +950,6 @@ static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, } #endif -void drm_bridge_debugfs_init(struct drm_minor *minor); +void drm_bridge_debugfs_init(struct drm_device *dev); #endif diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index c0a14b40c039..d47458ecdac4 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -195,6 +195,6 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); drm_for_each_connector_iter(connector, iter) \ if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) -void drm_client_debugfs_init(struct drm_minor *minor); +void drm_client_debugfs_init(struct drm_device *dev); #endif diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 7cf4afae2e79..3cf12b14232d 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -311,6 +311,13 @@ struct drm_device { */ struct drm_fb_helper *fb_helper; + /** + * @debugfs_root: + * + * Root directory for debugfs files. + */ + struct dentry *debugfs_root; + /** * @debugfs_mutex: * diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 9813fa759b75..9850fe73b739 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -581,4 +581,12 @@ static inline bool drm_firmware_drivers_only(void) return video_firmware_drivers_only(); } +#if defined(CONFIG_DEBUG_FS) +void drm_debugfs_dev_init(struct drm_device *dev, struct dentry *root); +#else +static void drm_debugfs_dev_init(struct drm_device *dev, struct dentry *root) +{ +} +#endif + #endif diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 010239392adf..12930a08368c 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -79,6 +79,7 @@ struct drm_minor { struct device *kdev; /* Linux device */ struct drm_device *dev; + struct dentry *debugfs_symlink; struct dentry *debugfs_root; struct list_head debugfs_list; -- cgit From ec9c7073bb082412a49466059053ace537c1a30d Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 29 Aug 2023 13:01:14 +0200 Subject: drm/debugfs: remove dev->debugfs_list and debugfs_mutex v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mutex was completely pointless in the first place since any parallel adding of files to this list would result in random behavior since the list is filled and consumed multiple times. Completely drop that approach and just create the files directly but return -ENODEV while opening the file when the minors are not registered yet. v2: rebase on debugfs directory rework, limit access before minors are registered. Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20230829110115.3442-5-christian.koenig@amd.com Reviewed-by: Andi Shyti --- include/drm/drm_device.h | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 3cf12b14232d..c490977ee250 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -318,21 +318,6 @@ struct drm_device { */ struct dentry *debugfs_root; - /** - * @debugfs_mutex: - * - * Protects &debugfs_list access. - */ - struct mutex debugfs_mutex; - - /** - * @debugfs_list: - * - * List of debugfs files to be created by the DRM device. The files - * must be added during drm_dev_register(). - */ - struct list_head debugfs_list; - /* Everything below here is for legacy driver, never use! */ /* private: */ #if IS_ENABLED(CONFIG_DRM_LEGACY) -- cgit From 8e455145d8f163aefa6b9cc29478e0a9f82276e6 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 29 Aug 2023 13:01:15 +0200 Subject: drm/debugfs: rework drm_debugfs_create_files implementation v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use managed memory allocation for this. That allows us to not keep track of all the files any more. v2: keep drm_debugfs_cleanup(), but rename to drm_debugfs_unregister(), we still need to cleanup the symlink Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20230829110115.3442-6-christian.koenig@amd.com Reviewed-by: Andi Shyti --- include/drm/drm_debugfs.h | 4 ++-- include/drm/drm_file.h | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index cb2c1956a214..7213ce15e4ff 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -142,8 +142,8 @@ struct drm_debugfs_entry { void drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root, struct drm_minor *minor); -int drm_debugfs_remove_files(const struct drm_info_list *files, - int count, struct drm_minor *minor); +int drm_debugfs_remove_files(const struct drm_info_list *files, int count, + struct dentry *root, struct drm_minor *minor); void drm_debugfs_add_file(struct drm_device *dev, const char *name, int (*show)(struct seq_file*, void*), void *data); diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 12930a08368c..489cc3758a82 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -81,9 +81,6 @@ struct drm_minor { struct dentry *debugfs_symlink; struct dentry *debugfs_root; - - struct list_head debugfs_list; - struct mutex debugfs_lock; /* Protects debugfs_list. */ }; /** -- cgit From d8dfccde2709de4327c3d62b50e5dc012f08836f Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 31 Aug 2023 09:09:36 +0100 Subject: drm/bridge: Drop conditionals around of_node pointers This patch is based on commit c9e358dfc4a8 ("driver-core: remove conditionals around devicetree pointers"). Having conditional around the of_node pointer of the drm_bridge structure turns out to make driver code use ugly #ifdef blocks. Drop the conditionals to simplify drivers. While this slightly increases the size of struct drm_bridge on non-OF system, the number of bridges used today and foreseen tomorrow on those systems is very low, so this shouldn't be an issue. So drop #if conditionals by adding struct device_node forward declaration. Suggested-by: Douglas Anderson Signed-off-by: Biju Das Reviewed-by: Douglas Anderson Reviewed-by: Laurent Pinchart Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20230831080938.47454-3-biju.das.jz@bp.renesas.com --- include/drm/drm_bridge.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2dd94224f17e..cfb7dcdb66c4 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -32,6 +32,8 @@ #include #include +struct device_node; + struct drm_bridge; struct drm_bridge_timings; struct drm_connector; @@ -716,10 +718,8 @@ struct drm_bridge { struct drm_encoder *encoder; /** @chain_node: used to form a bridge chain */ struct list_head chain_node; -#ifdef CONFIG_OF /** @of_node: device node pointer to the bridge */ struct device_node *of_node; -#endif /** @list: to keep track of all added bridges */ struct list_head list; /** -- cgit From d20b484c674d2eae816978a98fa38b4054aeca3b Mon Sep 17 00:00:00 2001 From: Thomas Hellström Date: Wed, 6 Sep 2023 11:50:39 +0200 Subject: drm/drm_exec: Work around a WW mutex lockdep oddity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If *any* object of a certain WW mutex class is locked, lockdep will consider *all* mutexes of that class as locked. Also the lock allocation tracking code will apparently register only the address of the first mutex of a given class locked in a sequence. This has the odd consequence that if that first mutex is unlocked while other mutexes of the same class remain locked and then its memory then freed, the lock alloc tracking code will incorrectly assume that memory is freed with a held lock in there. For now, work around that for drm_exec by releasing the first grabbed object lock last. v2: - Fix a typo (Danilo Krummrich) - Reword the commit message a bit. - Add a Fixes: tag Related lock alloc tracking warning: [ 322.660067] ========================= [ 322.660070] WARNING: held lock freed! [ 322.660074] 6.5.0-rc7+ #155 Tainted: G U N [ 322.660078] ------------------------- [ 322.660081] kunit_try_catch/4981 is freeing memory ffff888112adc000-ffff888112adc3ff, with a lock still held there! [ 322.660089] ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec] [ 322.660104] 2 locks held by kunit_try_catch/4981: [ 322.660108] #0: ffffc9000343fe18 (reservation_ww_class_acquire){+.+.}-{0:0}, at: test_early_put+0x22f/0x490 [drm_exec_test] [ 322.660123] #1: ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec] [ 322.660135] stack backtrace: [ 322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G U N 6.5.0-rc7+ #155 [ 322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021 [ 322.660152] Call Trace: [ 322.660155] [ 322.660158] dump_stack_lvl+0x57/0x90 [ 322.660164] debug_check_no_locks_freed+0x20b/0x2b0 [ 322.660172] slab_free_freelist_hook+0xa1/0x160 [ 322.660179] ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec] [ 322.660186] __kmem_cache_free+0xb2/0x290 [ 322.660192] drm_exec_unlock_all+0x168/0x2a0 [drm_exec] [ 322.660200] drm_exec_fini+0xf/0x1c0 [drm_exec] [ 322.660206] test_early_put+0x289/0x490 [drm_exec_test] [ 322.660215] ? __pfx_test_early_put+0x10/0x10 [drm_exec_test] [ 322.660222] ? __kasan_check_byte+0xf/0x40 [ 322.660227] ? __ksize+0x63/0x140 [ 322.660233] ? drmm_add_final_kfree+0x3e/0xa0 [drm] [ 322.660289] ? _raw_spin_unlock_irqrestore+0x30/0x60 [ 322.660294] ? lockdep_hardirqs_on+0x7d/0x100 [ 322.660301] ? __pfx_kunit_try_run_case+0x10/0x10 [kunit] [ 322.660310] ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit] [ 322.660319] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [ 322.660328] kthread+0x2e7/0x3c0 [ 322.660334] ? __pfx_kthread+0x10/0x10 [ 322.660339] ret_from_fork+0x2d/0x70 [ 322.660345] ? __pfx_kthread+0x10/0x10 [ 322.660349] ret_from_fork_asm+0x1b/0x30 [ 322.660358] [ 322.660818] ok 8 test_early_put Cc: Christian König Cc: Boris Brezillon Cc: Danilo Krummrich Cc: dri-devel@lists.freedesktop.org Fixes: 09593216bff1 ("drm: execution context for GEM buffers v7") Signed-off-by: Thomas Hellström Reviewed-by: Boris Brezillon Reviewed-by: Danilo Krummrich Reviewed-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20230906095039.3320-4-thomas.hellstrom@linux.intel.com --- include/drm/drm_exec.h | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index e0462361adf9..b5bf0b6da791 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -51,6 +51,20 @@ struct drm_exec { struct drm_gem_object *prelocked; }; +/** + * drm_exec_obj() - Return the object for a give drm_exec index + * @exec: Pointer to the drm_exec context + * @index: The index. + * + * Return: Pointer to the locked object corresponding to @index if + * index is within the number of locked objects. NULL otherwise. + */ +static inline struct drm_gem_object * +drm_exec_obj(struct drm_exec *exec, unsigned long index) +{ + return index < exec->num_objects ? exec->objects[index] : NULL; +} + /** * drm_exec_for_each_locked_object - iterate over all the locked objects * @exec: drm_exec object @@ -59,10 +73,23 @@ struct drm_exec { * * Iterate over all the locked GEM objects inside the drm_exec object. */ -#define drm_exec_for_each_locked_object(exec, index, obj) \ - for (index = 0, obj = (exec)->objects[0]; \ - index < (exec)->num_objects; \ - ++index, obj = (exec)->objects[index]) +#define drm_exec_for_each_locked_object(exec, index, obj) \ + for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index)) + +/** + * drm_exec_for_each_locked_object_reverse - iterate over all the locked + * objects in reverse locking order + * @exec: drm_exec object + * @index: unsigned long index for the iteration + * @obj: the current GEM object + * + * Iterate over all the locked GEM objects inside the drm_exec object in + * reverse locking order. Note that @index may go below zero and wrap, + * but that will be caught by drm_exec_obj(), returning a NULL object. + */ +#define drm_exec_for_each_locked_object_reverse(exec, index, obj) \ + for ((index) = (exec)->num_objects - 1; \ + ((obj) = drm_exec_obj(exec, index)); --(index)) /** * drm_exec_until_all_locked - loop until all GEM objects are locked -- cgit From 9eeba919dd0f524f73feeeef82f3ca877f9ccce4 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 6 Sep 2023 22:47:38 +0200 Subject: drm/connector: document DRM_MODE_COLORIMETRY_COUNT The drm_colorspace enum member DRM_MODE_COLORIMETRY_COUNT has been properly documented by moving the description out of the enum to the member description list to get rid of an additional warning and improve documentation clarity. Signed-off-by: Javier Carrasco Reviewed-by: Randy Dunlap Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20230906-topic-drm_connector_doc-v2-1-1f2dcaa43269@gmail.com --- include/drm/drm_connector.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index d300fde6c1a4..18cf46e3478b 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -498,6 +498,8 @@ enum drm_privacy_screen_status { * ITU-R BT.601 colorimetry format * The DP spec does not say whether this is the 525 or the 625 * line version. + * @DRM_MODE_COLORIMETRY_COUNT: + * Not a valid value; merely used four counting */ enum drm_colorspace { /* For Default case, driver will set the colorspace */ @@ -522,7 +524,6 @@ enum drm_colorspace { DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED = 13, DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT = 14, DRM_MODE_COLORIMETRY_BT601_YCC = 15, - /* not a valid value; merely used for counting */ DRM_MODE_COLORIMETRY_COUNT }; -- cgit From b88c168e6b91eefde1ba4cba19b0f3e3d735c3d2 Mon Sep 17 00:00:00 2001 From: Arthur Grillo Date: Fri, 1 Sep 2023 15:05:50 -0300 Subject: drm/debugfs: Add inline to drm_debugfs_dev_init() to suppres -Wunused-function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CONFIG_DEBUG_FS is not set -Wunused-function warnings appear, make the static function inline to suppress that. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202309012114.T8Vlfaf8-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202309012131.FeakBzEj-lkp@intel.com/ Signed-off-by: Arthur Grillo Reviewed-by: Andi Shyti Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal Link: https://patchwork.freedesktop.org/patch/msgid/20230901-debugfs-fix-unused-function-warning-v1-1-161dd0902975@riseup.net --- include/drm/drm_drv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 9850fe73b739..e2640dc64e08 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -584,7 +584,7 @@ static inline bool drm_firmware_drivers_only(void) #if defined(CONFIG_DEBUG_FS) void drm_debugfs_dev_init(struct drm_device *dev, struct dentry *root); #else -static void drm_debugfs_dev_init(struct drm_device *dev, struct dentry *root) +static inline void drm_debugfs_dev_init(struct drm_device *dev, struct dentry *root) { } #endif -- cgit From 5aa1dfcdf0a429e4941e2eef75b006a8c7a8ac49 Mon Sep 17 00:00:00 2001 From: Wayne Lin Date: Mon, 7 Aug 2023 10:56:38 +0800 Subject: drm/mst: Refactor the flow for payload allocation/removement [Why] Today, the allocation/deallocation steps and status is a bit unclear. For instance, payload->vc_start_slot = -1 stands for "the failure of updating DPCD payload ID table" and can also represent as "payload is not allocated yet". These two cases should be handled differently and hence better to distinguish them for better understanding. [How] Define enumeration - ALLOCATION_LOCAL, ALLOCATION_DFP and ALLOCATION_REMOTE to distinguish different allocation status. Adjust the code to handle different status accordingly for better understanding the sequence of payload allocation and payload removement. For payload creation, the procedure should look like this: DRM part 1: * step 1 - update sw mst mgr variables to add a new payload * step 2 - add payload at immediate DFP DPCD payload table Driver: * Add new payload in HW and sync up with DFP by sending ACT DRM Part 2: * Send ALLOCATE_PAYLOAD sideband message to allocate bandwidth along the virtual channel. And as for payload removement, the procedure should look like this: DRM part 1: * step 1 - Send ALLOCATE_PAYLOAD sideband message to release bandwidth along the virtual channel * step 2 - Clear payload allocation at immediate DFP DPCD payload table Driver: * Remove the payload in HW and sync up with DFP by sending ACT DRM part 2: * update sw mst mgr variables to remove the payload Note that it's fine to fail when communicate with the branch device connected at immediate downstrean-facing port, but updating variables of SW mst mgr and HW configuration should be conducted anyway. That's because it's under commit_tail and we need to complete the HW programming. Changes since v1: * Remove the set but not use variable 'old_payload' in function 'nv50_msto_prepare'. Catched by kernel test robot Signed-off-by: Wayne Lin Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20230807025639.1612361-3-Wayne.Lin@amd.com --- include/drm/display/drm_dp_mst_helper.h | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'include/drm') diff --git a/include/drm/display/drm_dp_mst_helper.h b/include/drm/display/drm_dp_mst_helper.h index ed5c9660563c..4429d3b1745b 100644 --- a/include/drm/display/drm_dp_mst_helper.h +++ b/include/drm/display/drm_dp_mst_helper.h @@ -46,6 +46,13 @@ struct drm_dp_mst_topology_ref_history { }; #endif /* IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) */ +enum drm_dp_mst_payload_allocation { + DRM_DP_MST_PAYLOAD_ALLOCATION_NONE, + DRM_DP_MST_PAYLOAD_ALLOCATION_LOCAL, + DRM_DP_MST_PAYLOAD_ALLOCATION_DFP, + DRM_DP_MST_PAYLOAD_ALLOCATION_REMOTE, +}; + struct drm_dp_mst_branch; /** @@ -537,7 +544,7 @@ struct drm_dp_mst_atomic_payload { * drm_dp_mst_atomic_wait_for_dependencies() has been called, which will ensure the * previous MST states payload start slots have been copied over to the new state. Note * that a new start slot won't be assigned/removed from this payload until - * drm_dp_add_payload_part1()/drm_dp_remove_payload() have been called. + * drm_dp_add_payload_part1()/drm_dp_remove_payload_part2() have been called. * * Acquire the MST modesetting lock, and then wait for any pending MST-related commits to * get committed to hardware by calling drm_crtc_commit_wait() on each of the * &drm_crtc_commit structs in &drm_dp_mst_topology_state.commit_deps. @@ -564,6 +571,9 @@ struct drm_dp_mst_atomic_payload { /** @dsc_enabled: Whether or not this payload has DSC enabled */ bool dsc_enabled : 1; + /** @payload_allocation_status: The allocation status of this payload */ + enum drm_dp_mst_payload_allocation payload_allocation_status; + /** @next: The list node for this payload */ struct list_head next; }; @@ -842,10 +852,13 @@ int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int drm_dp_add_payload_part2(struct drm_dp_mst_topology_mgr *mgr, struct drm_atomic_state *state, struct drm_dp_mst_atomic_payload *payload); -void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_topology_state *mst_state, - const struct drm_dp_mst_atomic_payload *old_payload, - struct drm_dp_mst_atomic_payload *new_payload); +void drm_dp_remove_payload_part1(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_atomic_payload *payload); +void drm_dp_remove_payload_part2(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + const struct drm_dp_mst_atomic_payload *old_payload, + struct drm_dp_mst_atomic_payload *new_payload); int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr); -- cgit From 0a1844bf0b532d84324453374ad6845f64066c28 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Sat, 9 Sep 2023 09:09:00 -0700 Subject: drm/buddy: Improve contiguous memory allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem statement: The current method roundup_power_of_two() to allocate contiguous address triggers -ENOSPC in some cases even though we have enough free spaces and so to help with that we introduce a try harder mechanism. In case of -ENOSPC, the new try harder mechanism rounddown the original size to power of 2 and iterating over the round down sized freelist blocks to allocate the required size traversing RHS and LHS. As part of the above new method implementation we moved contiguous/alignment size computation part and trim function to the drm buddy file. v2: Modify the alloc_range() function to return total allocated size on -ENOSPC err and traverse RHS/LHS to allocate the required size (Matthew). Signed-off-by: Arunpravin Paneer Selvam Link: https://patchwork.freedesktop.org/patch/msgid/20230909160902.15644-1-Arunpravin.PaneerSelvam@amd.com Reviewed-by: Matthew Auld Signed-off-by: Christian König --- include/drm/drm_buddy.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h index 572077ff8ae7..a5b39fc01003 100644 --- a/include/drm/drm_buddy.h +++ b/include/drm/drm_buddy.h @@ -22,8 +22,9 @@ start__ >= max__ || size__ > max__ - start__; \ }) -#define DRM_BUDDY_RANGE_ALLOCATION (1 << 0) -#define DRM_BUDDY_TOPDOWN_ALLOCATION (1 << 1) +#define DRM_BUDDY_RANGE_ALLOCATION BIT(0) +#define DRM_BUDDY_TOPDOWN_ALLOCATION BIT(1) +#define DRM_BUDDY_CONTIGUOUS_ALLOCATION BIT(2) struct drm_buddy_block { #define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12) @@ -155,5 +156,4 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p); void drm_buddy_block_print(struct drm_buddy *mm, struct drm_buddy_block *block, struct drm_printer *p); - #endif -- cgit From c286c48018dea3c3bea9813477631cb12d6199c6 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 13 Sep 2023 16:30:24 -0700 Subject: drm/debugfs: Fix drm_debugfs_remove_files() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building without CONFIG_DEBUG_FS: drivers/gpu/drm/tegra/dc.c:1757:59: error: too many arguments to function call, expected 3, have 4 1757 | drm_debugfs_remove_files(dc->debugfs_files, count, root, minor); | ~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~ include/drm/drm_debugfs.h:162:19: note: 'drm_debugfs_remove_files' declared here 162 | static inline int drm_debugfs_remove_files(const struct drm_info_list *files, | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 163 | int count, struct drm_minor *minor) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1 error generated. Update the stub to include the root parameter. Fixes: 8e455145d8f1 ("drm/debugfs: rework drm_debugfs_create_files implementation v2") Signed-off-by: Nathan Chancellor Reviewed-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20230913-fix-drm_debugfs_remove_files-stub-v1-1-6b952ac559f3@kernel.org Signed-off-by: Christian König --- include/drm/drm_debugfs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index 7213ce15e4ff..3bba169f9bae 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -160,7 +160,8 @@ static inline void drm_debugfs_create_files(const struct drm_info_list *files, {} static inline int drm_debugfs_remove_files(const struct drm_info_list *files, - int count, struct drm_minor *minor) + int count, struct dentry *root, + struct drm_minor *minor) { return 0; } -- cgit From 139a27854bf5ce93ff9805f9f7683b88c13074dc Mon Sep 17 00:00:00 2001 From: Thomas Hellström Date: Thu, 7 Sep 2023 15:53:38 +0200 Subject: drm/tests: helpers: Avoid a driver uaf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when using __drm_kunit_helper_alloc_drm_device() the driver may be dereferenced by device-managed resources up until the device is freed, which is typically later than the kunit-managed resource code frees it. Fix this by simply make the driver device-managed as well. In short, the sequence leading to the UAF is as follows: INIT: Code allocates a struct device as a kunit-managed resource. Code allocates a drm driver as a kunit-managed resource. Code allocates a drm device as a device-managed resource. EXIT: Kunit resource cleanup frees the drm driver Kunit resource cleanup puts the struct device, which starts a device-managed resource cleanup device-managed cleanup calls drm_dev_put() drm_dev_put() dereferences the (now freed) drm driver -> Boom. Related KASAN message: [55272.551542] ================================================================== [55272.551551] BUG: KASAN: slab-use-after-free in drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551603] Read of size 8 at addr ffff888127502828 by task kunit_try_catch/10353 [55272.551612] CPU: 4 PID: 10353 Comm: kunit_try_catch Tainted: G U N 6.5.0-rc7+ #155 [55272.551620] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021 [55272.551626] Call Trace: [55272.551629] [55272.551633] dump_stack_lvl+0x57/0x90 [55272.551639] print_report+0xcf/0x630 [55272.551645] ? _raw_spin_lock_irqsave+0x5f/0x70 [55272.551652] ? drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551694] kasan_report+0xd7/0x110 [55272.551699] ? drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551742] drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551783] devres_release_all+0x15d/0x1f0 [55272.551790] ? __pfx_devres_release_all+0x10/0x10 [55272.551797] device_unbind_cleanup+0x16/0x1a0 [55272.551802] device_release_driver_internal+0x3e5/0x540 [55272.551808] ? kobject_put+0x5d/0x4b0 [55272.551814] bus_remove_device+0x1f1/0x3f0 [55272.551819] device_del+0x342/0x910 [55272.551826] ? __pfx_device_del+0x10/0x10 [55272.551830] ? lock_release+0x339/0x5e0 [55272.551836] ? kunit_remove_resource+0x128/0x290 [kunit] [55272.551845] ? __pfx_lock_release+0x10/0x10 [55272.551851] platform_device_del.part.0+0x1f/0x1e0 [55272.551856] ? _raw_spin_unlock_irqrestore+0x30/0x60 [55272.551863] kunit_remove_resource+0x195/0x290 [kunit] [55272.551871] ? _raw_spin_unlock_irqrestore+0x30/0x60 [55272.551877] kunit_cleanup+0x78/0x120 [kunit] [55272.551885] ? __kthread_parkme+0xc1/0x1f0 [55272.551891] ? __pfx_kunit_try_run_case_cleanup+0x10/0x10 [kunit] [55272.551900] ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit] [55272.551909] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [55272.551919] kthread+0x2e7/0x3c0 [55272.551924] ? __pfx_kthread+0x10/0x10 [55272.551929] ret_from_fork+0x2d/0x70 [55272.551935] ? __pfx_kthread+0x10/0x10 [55272.551940] ret_from_fork_asm+0x1b/0x30 [55272.551948] [55272.551953] Allocated by task 10351: [55272.551956] kasan_save_stack+0x1c/0x40 [55272.551962] kasan_set_track+0x21/0x30 [55272.551966] __kasan_kmalloc+0x8b/0x90 [55272.551970] __kmalloc+0x5e/0x160 [55272.551976] kunit_kmalloc_array+0x1c/0x50 [kunit] [55272.551984] drm_exec_test_init+0xfa/0x2c0 [drm_exec_test] [55272.551991] kunit_try_run_case+0xdd/0x250 [kunit] [55272.551999] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [55272.552008] kthread+0x2e7/0x3c0 [55272.552012] ret_from_fork+0x2d/0x70 [55272.552017] ret_from_fork_asm+0x1b/0x30 [55272.552024] Freed by task 10353: [55272.552027] kasan_save_stack+0x1c/0x40 [55272.552032] kasan_set_track+0x21/0x30 [55272.552036] kasan_save_free_info+0x27/0x40 [55272.552041] __kasan_slab_free+0x106/0x180 [55272.552046] slab_free_freelist_hook+0xb3/0x160 [55272.552051] __kmem_cache_free+0xb2/0x290 [55272.552056] kunit_remove_resource+0x195/0x290 [kunit] [55272.552064] kunit_cleanup+0x78/0x120 [kunit] [55272.552072] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [55272.552080] kthread+0x2e7/0x3c0 [55272.552085] ret_from_fork+0x2d/0x70 [55272.552089] ret_from_fork_asm+0x1b/0x30 [55272.552096] The buggy address belongs to the object at ffff888127502800 which belongs to the cache kmalloc-512 of size 512 [55272.552105] The buggy address is located 40 bytes inside of freed 512-byte region [ffff888127502800, ffff888127502a00) [55272.552115] The buggy address belongs to the physical page: [55272.552119] page:00000000af6c70ff refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x127500 [55272.552127] head:00000000af6c70ff order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [55272.552133] anon flags: 0x17ffffc0010200(slab|head|node=0|zone=2|lastcpupid=0x1fffff) [55272.552141] page_type: 0xffffffff() [55272.552145] raw: 0017ffffc0010200 ffff888100042c80 0000000000000000 dead000000000001 [55272.552152] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000 [55272.552157] page dumped because: kasan: bad access detected [55272.552163] Memory state around the buggy address: [55272.552167] ffff888127502700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [55272.552173] ffff888127502780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [55272.552178] >ffff888127502800: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [55272.552184] ^ [55272.552187] ffff888127502880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [55272.552193] ffff888127502900: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [55272.552198] ================================================================== [55272.552203] Disabling lock debugging due to kernel taint v2: - Update commit message, add Fixes: tag and Cc stable. v3: - Further commit message updates (Maxime Ripard). Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Cc: stable@vger.kernel.org # v6.3+ Fixes: d98780310719 ("drm/tests: helpers: Allow to pass a custom drm_driver") Signed-off-by: Thomas Hellström Reviewed-by: Francois Dugast Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20230907135339.7971-2-thomas.hellstrom@linux.intel.com Signed-off-by: Maxime Ripard --- include/drm/drm_kunit_helpers.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 514c8a7a32f0..ba483c87f0e7 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -3,6 +3,8 @@ #ifndef DRM_KUNIT_HELPERS_H_ #define DRM_KUNIT_HELPERS_H_ +#include + #include struct drm_device; @@ -51,7 +53,7 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test, { struct drm_driver *driver; - driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); + driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, driver); driver->driver_features = features; -- cgit From 1c7a387ffef894b1ab3942f0482dac7a6e0a909c Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Wed, 21 Jun 2023 10:48:24 +0100 Subject: drm: Update file owner during use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the typical model where the display server opens the file descriptor and then hands it over to the client(*), we were showing stale data in debugfs. Fix it by updating the drm_file->pid on ioctl access from a different process. The field is also made RCU protected to allow for lockless readers. Update side is protected with dev->filelist_mutex. Before: $ cat /sys/kernel/debug/dri/0/clients command pid dev master a uid magic Xorg 2344 0 y y 0 0 Xorg 2344 0 n y 0 2 Xorg 2344 0 n y 0 3 Xorg 2344 0 n y 0 4 After: $ cat /sys/kernel/debug/dri/0/clients command tgid dev master a uid magic Xorg 830 0 y y 0 0 xfce4-session 880 0 n y 0 1 xfwm4 943 0 n y 0 2 neverball 1095 0 n y 0 3 *) More detailed and historically accurate description of various handover implementation kindly provided by Emil Velikov: """ The traditional model, the server was the orchestrator managing the primary device node. From the fd, to the master status and authentication. But looking at the fd alone, this has varied across the years. IIRC in the DRI1 days, Xorg (libdrm really) would have a list of open fd(s) and reuse those whenever needed, DRI2 the client was responsible for open() themselves and with DRI3 the fd was passed to the client. Around the inception of DRI3 and systemd-logind, the latter became another possible orchestrator. Whereby Xorg and Wayland compositors could ask it for the fd. For various reasons (hysterical and genuine ones) Xorg has a fallback path going the open(), whereas Wayland compositors are moving to solely relying on logind... some never had fallback even. Over the past few years, more projects have emerged which provide functionality similar (be that on API level, Dbus, or otherwise) to systemd-logind. """ v2: * Fixed typo in commit text and added a fine historical explanation from Emil. Signed-off-by: Tvrtko Ursulin Cc: "Christian König" Cc: Daniel Vetter Acked-by: Christian König Reviewed-by: Emil Velikov Reviewed-by: Rob Clark Tested-by: Rob Clark Link: https://patchwork.freedesktop.org/patch/msgid/20230621094824.2348732-1-tvrtko.ursulin@linux.intel.com Signed-off-by: Christian König --- include/drm/drm_file.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 489cc3758a82..e1b5b4282f75 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -254,8 +254,15 @@ struct drm_file { /** @master_lookup_lock: Serializes @master. */ spinlock_t master_lookup_lock; - /** @pid: Process that opened this file. */ - struct pid *pid; + /** + * @pid: Process that is using this file. + * + * Must only be dereferenced under a rcu_read_lock or equivalent. + * + * Updates are guarded with dev->filelist_mutex and reference must be + * dropped after a RCU grace period to accommodate lockless readers. + */ + struct pid __rcu *pid; /** @client_id: A unique id for fdinfo */ u64 client_id; @@ -418,6 +425,8 @@ static inline bool drm_is_accel_client(const struct drm_file *file_priv) return file_priv->minor->type == DRM_MINOR_ACCEL; } +void drm_file_update_pid(struct drm_file *); + int drm_open(struct inode *inode, struct file *filp); int drm_open_helper(struct file *filp, struct drm_minor *minor); ssize_t drm_read(struct file *filp, char __user *buffer, -- cgit From f72c2db47080523d5e0f3c20846c96ed31c35648 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 20 Sep 2023 16:42:34 +0200 Subject: drm/gpuvm: rename struct drm_gpuva_manager to struct drm_gpuvm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename struct drm_gpuva_manager to struct drm_gpuvm including corresponding functions. This way the GPUVA manager's structures align very well with the documentation of VM_BIND [1] and VM_BIND locking [2]. It also provides a better foundation for the naming of data structures and functions introduced for implementing a common dma-resv per GPU-VM including tracking of external and evicted objects in subsequent patches. [1] Documentation/gpu/drm-vm-bind-async.rst [2] Documentation/gpu/drm-vm-bind-locking.rst Cc: Thomas Hellström Cc: Matthew Brost Acked-by: Dave Airlie Acked-by: Christian König Signed-off-by: Danilo Krummrich Link: https://patchwork.freedesktop.org/patch/msgid/20230920144343.64830-2-dakr@redhat.com --- include/drm/drm_debugfs.h | 6 +- include/drm/drm_gpuva_mgr.h | 706 -------------------------------------------- include/drm/drm_gpuvm.h | 705 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 708 insertions(+), 709 deletions(-) delete mode 100644 include/drm/drm_gpuva_mgr.h create mode 100644 include/drm/drm_gpuvm.h (limited to 'include/drm') diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index 3bba169f9bae..cf06cee4343f 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -35,7 +35,7 @@ #include #include -#include +#include /** * DRM_DEBUGFS_GPUVA_INFO - &drm_info_list entry to dump a GPU VA space @@ -152,7 +152,7 @@ void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count); int drm_debugfs_gpuva_info(struct seq_file *m, - struct drm_gpuva_manager *mgr); + struct drm_gpuvm *gpuvm); #else static inline void drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root, @@ -177,7 +177,7 @@ static inline void drm_debugfs_add_files(struct drm_device *dev, {} static inline int drm_debugfs_gpuva_info(struct seq_file *m, - struct drm_gpuva_manager *mgr) + struct drm_gpuvm *gpuvm) { return 0; } diff --git a/include/drm/drm_gpuva_mgr.h b/include/drm/drm_gpuva_mgr.h deleted file mode 100644 index ed8d50200cc3..000000000000 --- a/include/drm/drm_gpuva_mgr.h +++ /dev/null @@ -1,706 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#ifndef __DRM_GPUVA_MGR_H__ -#define __DRM_GPUVA_MGR_H__ - -/* - * Copyright (c) 2022 Red Hat. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include - -#include - -struct drm_gpuva_manager; -struct drm_gpuva_fn_ops; - -/** - * enum drm_gpuva_flags - flags for struct drm_gpuva - */ -enum drm_gpuva_flags { - /** - * @DRM_GPUVA_INVALIDATED: - * - * Flag indicating that the &drm_gpuva's backing GEM is invalidated. - */ - DRM_GPUVA_INVALIDATED = (1 << 0), - - /** - * @DRM_GPUVA_SPARSE: - * - * Flag indicating that the &drm_gpuva is a sparse mapping. - */ - DRM_GPUVA_SPARSE = (1 << 1), - - /** - * @DRM_GPUVA_USERBITS: user defined bits - */ - DRM_GPUVA_USERBITS = (1 << 2), -}; - -/** - * struct drm_gpuva - structure to track a GPU VA mapping - * - * This structure represents a GPU VA mapping and is associated with a - * &drm_gpuva_manager. - * - * Typically, this structure is embedded in bigger driver structures. - */ -struct drm_gpuva { - /** - * @mgr: the &drm_gpuva_manager this object is associated with - */ - struct drm_gpuva_manager *mgr; - - /** - * @flags: the &drm_gpuva_flags for this mapping - */ - enum drm_gpuva_flags flags; - - /** - * @va: structure containing the address and range of the &drm_gpuva - */ - struct { - /** - * @addr: the start address - */ - u64 addr; - - /* - * @range: the range - */ - u64 range; - } va; - - /** - * @gem: structure containing the &drm_gem_object and it's offset - */ - struct { - /** - * @offset: the offset within the &drm_gem_object - */ - u64 offset; - - /** - * @obj: the mapped &drm_gem_object - */ - struct drm_gem_object *obj; - - /** - * @entry: the &list_head to attach this object to a &drm_gem_object - */ - struct list_head entry; - } gem; - - /** - * @rb: structure containing data to store &drm_gpuvas in a rb-tree - */ - struct { - /** - * @rb: the rb-tree node - */ - struct rb_node node; - - /** - * @entry: The &list_head to additionally connect &drm_gpuvas - * in the same order they appear in the interval tree. This is - * useful to keep iterating &drm_gpuvas from a start node found - * through the rb-tree while doing modifications on the rb-tree - * itself. - */ - struct list_head entry; - - /** - * @__subtree_last: needed by the interval tree, holding last-in-subtree - */ - u64 __subtree_last; - } rb; -}; - -int drm_gpuva_insert(struct drm_gpuva_manager *mgr, struct drm_gpuva *va); -void drm_gpuva_remove(struct drm_gpuva *va); - -void drm_gpuva_link(struct drm_gpuva *va); -void drm_gpuva_unlink(struct drm_gpuva *va); - -struct drm_gpuva *drm_gpuva_find(struct drm_gpuva_manager *mgr, - u64 addr, u64 range); -struct drm_gpuva *drm_gpuva_find_first(struct drm_gpuva_manager *mgr, - u64 addr, u64 range); -struct drm_gpuva *drm_gpuva_find_prev(struct drm_gpuva_manager *mgr, u64 start); -struct drm_gpuva *drm_gpuva_find_next(struct drm_gpuva_manager *mgr, u64 end); - -bool drm_gpuva_interval_empty(struct drm_gpuva_manager *mgr, u64 addr, u64 range); - -static inline void drm_gpuva_init(struct drm_gpuva *va, u64 addr, u64 range, - struct drm_gem_object *obj, u64 offset) -{ - va->va.addr = addr; - va->va.range = range; - va->gem.obj = obj; - va->gem.offset = offset; -} - -/** - * drm_gpuva_invalidate() - sets whether the backing GEM of this &drm_gpuva is - * invalidated - * @va: the &drm_gpuva to set the invalidate flag for - * @invalidate: indicates whether the &drm_gpuva is invalidated - */ -static inline void drm_gpuva_invalidate(struct drm_gpuva *va, bool invalidate) -{ - if (invalidate) - va->flags |= DRM_GPUVA_INVALIDATED; - else - va->flags &= ~DRM_GPUVA_INVALIDATED; -} - -/** - * drm_gpuva_invalidated() - indicates whether the backing BO of this &drm_gpuva - * is invalidated - * @va: the &drm_gpuva to check - */ -static inline bool drm_gpuva_invalidated(struct drm_gpuva *va) -{ - return va->flags & DRM_GPUVA_INVALIDATED; -} - -/** - * struct drm_gpuva_manager - DRM GPU VA Manager - * - * The DRM GPU VA Manager keeps track of a GPU's virtual address space by using - * &maple_tree structures. Typically, this structure is embedded in bigger - * driver structures. - * - * Drivers can pass addresses and ranges in an arbitrary unit, e.g. bytes or - * pages. - * - * There should be one manager instance per GPU virtual address space. - */ -struct drm_gpuva_manager { - /** - * @name: the name of the DRM GPU VA space - */ - const char *name; - - /** - * @mm_start: start of the VA space - */ - u64 mm_start; - - /** - * @mm_range: length of the VA space - */ - u64 mm_range; - - /** - * @rb: structures to track &drm_gpuva entries - */ - struct { - /** - * @tree: the rb-tree to track GPU VA mappings - */ - struct rb_root_cached tree; - - /** - * @list: the &list_head to track GPU VA mappings - */ - struct list_head list; - } rb; - - /** - * @kernel_alloc_node: - * - * &drm_gpuva representing the address space cutout reserved for - * the kernel - */ - struct drm_gpuva kernel_alloc_node; - - /** - * @ops: &drm_gpuva_fn_ops providing the split/merge steps to drivers - */ - const struct drm_gpuva_fn_ops *ops; -}; - -void drm_gpuva_manager_init(struct drm_gpuva_manager *mgr, - const char *name, - u64 start_offset, u64 range, - u64 reserve_offset, u64 reserve_range, - const struct drm_gpuva_fn_ops *ops); -void drm_gpuva_manager_destroy(struct drm_gpuva_manager *mgr); - -static inline struct drm_gpuva * -__drm_gpuva_next(struct drm_gpuva *va) -{ - if (va && !list_is_last(&va->rb.entry, &va->mgr->rb.list)) - return list_next_entry(va, rb.entry); - - return NULL; -} - -/** - * drm_gpuva_for_each_va_range() - iterate over a range of &drm_gpuvas - * @va__: &drm_gpuva structure to assign to in each iteration step - * @mgr__: &drm_gpuva_manager to walk over - * @start__: starting offset, the first gpuva will overlap this - * @end__: ending offset, the last gpuva will start before this (but may - * overlap) - * - * This iterator walks over all &drm_gpuvas in the &drm_gpuva_manager that lie - * between @start__ and @end__. It is implemented similarly to list_for_each(), - * but is using the &drm_gpuva_manager's internal interval tree to accelerate - * the search for the starting &drm_gpuva, and hence isn't safe against removal - * of elements. It assumes that @end__ is within (or is the upper limit of) the - * &drm_gpuva_manager. This iterator does not skip over the &drm_gpuva_manager's - * @kernel_alloc_node. - */ -#define drm_gpuva_for_each_va_range(va__, mgr__, start__, end__) \ - for (va__ = drm_gpuva_find_first((mgr__), (start__), (end__) - (start__)); \ - va__ && (va__->va.addr < (end__)); \ - va__ = __drm_gpuva_next(va__)) - -/** - * drm_gpuva_for_each_va_range_safe() - safely iterate over a range of - * &drm_gpuvas - * @va__: &drm_gpuva to assign to in each iteration step - * @next__: another &drm_gpuva to use as temporary storage - * @mgr__: &drm_gpuva_manager to walk over - * @start__: starting offset, the first gpuva will overlap this - * @end__: ending offset, the last gpuva will start before this (but may - * overlap) - * - * This iterator walks over all &drm_gpuvas in the &drm_gpuva_manager that lie - * between @start__ and @end__. It is implemented similarly to - * list_for_each_safe(), but is using the &drm_gpuva_manager's internal interval - * tree to accelerate the search for the starting &drm_gpuva, and hence is safe - * against removal of elements. It assumes that @end__ is within (or is the - * upper limit of) the &drm_gpuva_manager. This iterator does not skip over the - * &drm_gpuva_manager's @kernel_alloc_node. - */ -#define drm_gpuva_for_each_va_range_safe(va__, next__, mgr__, start__, end__) \ - for (va__ = drm_gpuva_find_first((mgr__), (start__), (end__) - (start__)), \ - next__ = __drm_gpuva_next(va__); \ - va__ && (va__->va.addr < (end__)); \ - va__ = next__, next__ = __drm_gpuva_next(va__)) - -/** - * drm_gpuva_for_each_va() - iterate over all &drm_gpuvas - * @va__: &drm_gpuva to assign to in each iteration step - * @mgr__: &drm_gpuva_manager to walk over - * - * This iterator walks over all &drm_gpuva structures associated with the given - * &drm_gpuva_manager. - */ -#define drm_gpuva_for_each_va(va__, mgr__) \ - list_for_each_entry(va__, &(mgr__)->rb.list, rb.entry) - -/** - * drm_gpuva_for_each_va_safe() - safely iterate over all &drm_gpuvas - * @va__: &drm_gpuva to assign to in each iteration step - * @next__: another &drm_gpuva to use as temporary storage - * @mgr__: &drm_gpuva_manager to walk over - * - * This iterator walks over all &drm_gpuva structures associated with the given - * &drm_gpuva_manager. It is implemented with list_for_each_entry_safe(), and - * hence safe against the removal of elements. - */ -#define drm_gpuva_for_each_va_safe(va__, next__, mgr__) \ - list_for_each_entry_safe(va__, next__, &(mgr__)->rb.list, rb.entry) - -/** - * enum drm_gpuva_op_type - GPU VA operation type - * - * Operations to alter the GPU VA mappings tracked by the &drm_gpuva_manager. - */ -enum drm_gpuva_op_type { - /** - * @DRM_GPUVA_OP_MAP: the map op type - */ - DRM_GPUVA_OP_MAP, - - /** - * @DRM_GPUVA_OP_REMAP: the remap op type - */ - DRM_GPUVA_OP_REMAP, - - /** - * @DRM_GPUVA_OP_UNMAP: the unmap op type - */ - DRM_GPUVA_OP_UNMAP, - - /** - * @DRM_GPUVA_OP_PREFETCH: the prefetch op type - */ - DRM_GPUVA_OP_PREFETCH, -}; - -/** - * struct drm_gpuva_op_map - GPU VA map operation - * - * This structure represents a single map operation generated by the - * DRM GPU VA manager. - */ -struct drm_gpuva_op_map { - /** - * @va: structure containing address and range of a map - * operation - */ - struct { - /** - * @addr: the base address of the new mapping - */ - u64 addr; - - /** - * @range: the range of the new mapping - */ - u64 range; - } va; - - /** - * @gem: structure containing the &drm_gem_object and it's offset - */ - struct { - /** - * @offset: the offset within the &drm_gem_object - */ - u64 offset; - - /** - * @obj: the &drm_gem_object to map - */ - struct drm_gem_object *obj; - } gem; -}; - -/** - * struct drm_gpuva_op_unmap - GPU VA unmap operation - * - * This structure represents a single unmap operation generated by the - * DRM GPU VA manager. - */ -struct drm_gpuva_op_unmap { - /** - * @va: the &drm_gpuva to unmap - */ - struct drm_gpuva *va; - - /** - * @keep: - * - * Indicates whether this &drm_gpuva is physically contiguous with the - * original mapping request. - * - * Optionally, if &keep is set, drivers may keep the actual page table - * mappings for this &drm_gpuva, adding the missing page table entries - * only and update the &drm_gpuva_manager accordingly. - */ - bool keep; -}; - -/** - * struct drm_gpuva_op_remap - GPU VA remap operation - * - * This represents a single remap operation generated by the DRM GPU VA manager. - * - * A remap operation is generated when an existing GPU VA mmapping is split up - * by inserting a new GPU VA mapping or by partially unmapping existent - * mapping(s), hence it consists of a maximum of two map and one unmap - * operation. - * - * The @unmap operation takes care of removing the original existing mapping. - * @prev is used to remap the preceding part, @next the subsequent part. - * - * If either a new mapping's start address is aligned with the start address - * of the old mapping or the new mapping's end address is aligned with the - * end address of the old mapping, either @prev or @next is NULL. - * - * Note, the reason for a dedicated remap operation, rather than arbitrary - * unmap and map operations, is to give drivers the chance of extracting driver - * specific data for creating the new mappings from the unmap operations's - * &drm_gpuva structure which typically is embedded in larger driver specific - * structures. - */ -struct drm_gpuva_op_remap { - /** - * @prev: the preceding part of a split mapping - */ - struct drm_gpuva_op_map *prev; - - /** - * @next: the subsequent part of a split mapping - */ - struct drm_gpuva_op_map *next; - - /** - * @unmap: the unmap operation for the original existing mapping - */ - struct drm_gpuva_op_unmap *unmap; -}; - -/** - * struct drm_gpuva_op_prefetch - GPU VA prefetch operation - * - * This structure represents a single prefetch operation generated by the - * DRM GPU VA manager. - */ -struct drm_gpuva_op_prefetch { - /** - * @va: the &drm_gpuva to prefetch - */ - struct drm_gpuva *va; -}; - -/** - * struct drm_gpuva_op - GPU VA operation - * - * This structure represents a single generic operation. - * - * The particular type of the operation is defined by @op. - */ -struct drm_gpuva_op { - /** - * @entry: - * - * The &list_head used to distribute instances of this struct within - * &drm_gpuva_ops. - */ - struct list_head entry; - - /** - * @op: the type of the operation - */ - enum drm_gpuva_op_type op; - - union { - /** - * @map: the map operation - */ - struct drm_gpuva_op_map map; - - /** - * @remap: the remap operation - */ - struct drm_gpuva_op_remap remap; - - /** - * @unmap: the unmap operation - */ - struct drm_gpuva_op_unmap unmap; - - /** - * @prefetch: the prefetch operation - */ - struct drm_gpuva_op_prefetch prefetch; - }; -}; - -/** - * struct drm_gpuva_ops - wraps a list of &drm_gpuva_op - */ -struct drm_gpuva_ops { - /** - * @list: the &list_head - */ - struct list_head list; -}; - -/** - * drm_gpuva_for_each_op() - iterator to walk over &drm_gpuva_ops - * @op: &drm_gpuva_op to assign in each iteration step - * @ops: &drm_gpuva_ops to walk - * - * This iterator walks over all ops within a given list of operations. - */ -#define drm_gpuva_for_each_op(op, ops) list_for_each_entry(op, &(ops)->list, entry) - -/** - * drm_gpuva_for_each_op_safe() - iterator to safely walk over &drm_gpuva_ops - * @op: &drm_gpuva_op to assign in each iteration step - * @next: &next &drm_gpuva_op to store the next step - * @ops: &drm_gpuva_ops to walk - * - * This iterator walks over all ops within a given list of operations. It is - * implemented with list_for_each_safe(), so save against removal of elements. - */ -#define drm_gpuva_for_each_op_safe(op, next, ops) \ - list_for_each_entry_safe(op, next, &(ops)->list, entry) - -/** - * drm_gpuva_for_each_op_from_reverse() - iterate backwards from the given point - * @op: &drm_gpuva_op to assign in each iteration step - * @ops: &drm_gpuva_ops to walk - * - * This iterator walks over all ops within a given list of operations beginning - * from the given operation in reverse order. - */ -#define drm_gpuva_for_each_op_from_reverse(op, ops) \ - list_for_each_entry_from_reverse(op, &(ops)->list, entry) - -/** - * drm_gpuva_first_op() - returns the first &drm_gpuva_op from &drm_gpuva_ops - * @ops: the &drm_gpuva_ops to get the fist &drm_gpuva_op from - */ -#define drm_gpuva_first_op(ops) \ - list_first_entry(&(ops)->list, struct drm_gpuva_op, entry) - -/** - * drm_gpuva_last_op() - returns the last &drm_gpuva_op from &drm_gpuva_ops - * @ops: the &drm_gpuva_ops to get the last &drm_gpuva_op from - */ -#define drm_gpuva_last_op(ops) \ - list_last_entry(&(ops)->list, struct drm_gpuva_op, entry) - -/** - * drm_gpuva_prev_op() - previous &drm_gpuva_op in the list - * @op: the current &drm_gpuva_op - */ -#define drm_gpuva_prev_op(op) list_prev_entry(op, entry) - -/** - * drm_gpuva_next_op() - next &drm_gpuva_op in the list - * @op: the current &drm_gpuva_op - */ -#define drm_gpuva_next_op(op) list_next_entry(op, entry) - -struct drm_gpuva_ops * -drm_gpuva_sm_map_ops_create(struct drm_gpuva_manager *mgr, - u64 addr, u64 range, - struct drm_gem_object *obj, u64 offset); -struct drm_gpuva_ops * -drm_gpuva_sm_unmap_ops_create(struct drm_gpuva_manager *mgr, - u64 addr, u64 range); - -struct drm_gpuva_ops * -drm_gpuva_prefetch_ops_create(struct drm_gpuva_manager *mgr, - u64 addr, u64 range); - -struct drm_gpuva_ops * -drm_gpuva_gem_unmap_ops_create(struct drm_gpuva_manager *mgr, - struct drm_gem_object *obj); - -void drm_gpuva_ops_free(struct drm_gpuva_manager *mgr, - struct drm_gpuva_ops *ops); - -static inline void drm_gpuva_init_from_op(struct drm_gpuva *va, - struct drm_gpuva_op_map *op) -{ - drm_gpuva_init(va, op->va.addr, op->va.range, - op->gem.obj, op->gem.offset); -} - -/** - * struct drm_gpuva_fn_ops - callbacks for split/merge steps - * - * This structure defines the callbacks used by &drm_gpuva_sm_map and - * &drm_gpuva_sm_unmap to provide the split/merge steps for map and unmap - * operations to drivers. - */ -struct drm_gpuva_fn_ops { - /** - * @op_alloc: called when the &drm_gpuva_manager allocates - * a struct drm_gpuva_op - * - * Some drivers may want to embed struct drm_gpuva_op into driver - * specific structures. By implementing this callback drivers can - * allocate memory accordingly. - * - * This callback is optional. - */ - struct drm_gpuva_op *(*op_alloc)(void); - - /** - * @op_free: called when the &drm_gpuva_manager frees a - * struct drm_gpuva_op - * - * Some drivers may want to embed struct drm_gpuva_op into driver - * specific structures. By implementing this callback drivers can - * free the previously allocated memory accordingly. - * - * This callback is optional. - */ - void (*op_free)(struct drm_gpuva_op *op); - - /** - * @sm_step_map: called from &drm_gpuva_sm_map to finally insert the - * mapping once all previous steps were completed - * - * The &priv pointer matches the one the driver passed to - * &drm_gpuva_sm_map or &drm_gpuva_sm_unmap, respectively. - * - * Can be NULL if &drm_gpuva_sm_map is used. - */ - int (*sm_step_map)(struct drm_gpuva_op *op, void *priv); - - /** - * @sm_step_remap: called from &drm_gpuva_sm_map and - * &drm_gpuva_sm_unmap to split up an existent mapping - * - * This callback is called when existent mapping needs to be split up. - * This is the case when either a newly requested mapping overlaps or - * is enclosed by an existent mapping or a partial unmap of an existent - * mapping is requested. - * - * The &priv pointer matches the one the driver passed to - * &drm_gpuva_sm_map or &drm_gpuva_sm_unmap, respectively. - * - * Can be NULL if neither &drm_gpuva_sm_map nor &drm_gpuva_sm_unmap is - * used. - */ - int (*sm_step_remap)(struct drm_gpuva_op *op, void *priv); - - /** - * @sm_step_unmap: called from &drm_gpuva_sm_map and - * &drm_gpuva_sm_unmap to unmap an existent mapping - * - * This callback is called when existent mapping needs to be unmapped. - * This is the case when either a newly requested mapping encloses an - * existent mapping or an unmap of an existent mapping is requested. - * - * The &priv pointer matches the one the driver passed to - * &drm_gpuva_sm_map or &drm_gpuva_sm_unmap, respectively. - * - * Can be NULL if neither &drm_gpuva_sm_map nor &drm_gpuva_sm_unmap is - * used. - */ - int (*sm_step_unmap)(struct drm_gpuva_op *op, void *priv); -}; - -int drm_gpuva_sm_map(struct drm_gpuva_manager *mgr, void *priv, - u64 addr, u64 range, - struct drm_gem_object *obj, u64 offset); - -int drm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr, void *priv, - u64 addr, u64 range); - -void drm_gpuva_map(struct drm_gpuva_manager *mgr, - struct drm_gpuva *va, - struct drm_gpuva_op_map *op); - -void drm_gpuva_remap(struct drm_gpuva *prev, - struct drm_gpuva *next, - struct drm_gpuva_op_remap *op); - -void drm_gpuva_unmap(struct drm_gpuva_op_unmap *op); - -#endif /* __DRM_GPUVA_MGR_H__ */ diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h new file mode 100644 index 000000000000..c7ed6bf441d4 --- /dev/null +++ b/include/drm/drm_gpuvm.h @@ -0,0 +1,705 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRM_GPUVM_H__ +#define __DRM_GPUVM_H__ + +/* + * Copyright (c) 2022 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +struct drm_gpuvm; +struct drm_gpuvm_ops; + +/** + * enum drm_gpuva_flags - flags for struct drm_gpuva + */ +enum drm_gpuva_flags { + /** + * @DRM_GPUVA_INVALIDATED: + * + * Flag indicating that the &drm_gpuva's backing GEM is invalidated. + */ + DRM_GPUVA_INVALIDATED = (1 << 0), + + /** + * @DRM_GPUVA_SPARSE: + * + * Flag indicating that the &drm_gpuva is a sparse mapping. + */ + DRM_GPUVA_SPARSE = (1 << 1), + + /** + * @DRM_GPUVA_USERBITS: user defined bits + */ + DRM_GPUVA_USERBITS = (1 << 2), +}; + +/** + * struct drm_gpuva - structure to track a GPU VA mapping + * + * This structure represents a GPU VA mapping and is associated with a + * &drm_gpuvm. + * + * Typically, this structure is embedded in bigger driver structures. + */ +struct drm_gpuva { + /** + * @vm: the &drm_gpuvm this object is associated with + */ + struct drm_gpuvm *vm; + + /** + * @flags: the &drm_gpuva_flags for this mapping + */ + enum drm_gpuva_flags flags; + + /** + * @va: structure containing the address and range of the &drm_gpuva + */ + struct { + /** + * @addr: the start address + */ + u64 addr; + + /* + * @range: the range + */ + u64 range; + } va; + + /** + * @gem: structure containing the &drm_gem_object and it's offset + */ + struct { + /** + * @offset: the offset within the &drm_gem_object + */ + u64 offset; + + /** + * @obj: the mapped &drm_gem_object + */ + struct drm_gem_object *obj; + + /** + * @entry: the &list_head to attach this object to a &drm_gem_object + */ + struct list_head entry; + } gem; + + /** + * @rb: structure containing data to store &drm_gpuvas in a rb-tree + */ + struct { + /** + * @rb: the rb-tree node + */ + struct rb_node node; + + /** + * @entry: The &list_head to additionally connect &drm_gpuvas + * in the same order they appear in the interval tree. This is + * useful to keep iterating &drm_gpuvas from a start node found + * through the rb-tree while doing modifications on the rb-tree + * itself. + */ + struct list_head entry; + + /** + * @__subtree_last: needed by the interval tree, holding last-in-subtree + */ + u64 __subtree_last; + } rb; +}; + +int drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va); +void drm_gpuva_remove(struct drm_gpuva *va); + +void drm_gpuva_link(struct drm_gpuva *va); +void drm_gpuva_unlink(struct drm_gpuva *va); + +struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); +struct drm_gpuva *drm_gpuva_find_first(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); +struct drm_gpuva *drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start); +struct drm_gpuva *drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end); + +static inline void drm_gpuva_init(struct drm_gpuva *va, u64 addr, u64 range, + struct drm_gem_object *obj, u64 offset) +{ + va->va.addr = addr; + va->va.range = range; + va->gem.obj = obj; + va->gem.offset = offset; +} + +/** + * drm_gpuva_invalidate() - sets whether the backing GEM of this &drm_gpuva is + * invalidated + * @va: the &drm_gpuva to set the invalidate flag for + * @invalidate: indicates whether the &drm_gpuva is invalidated + */ +static inline void drm_gpuva_invalidate(struct drm_gpuva *va, bool invalidate) +{ + if (invalidate) + va->flags |= DRM_GPUVA_INVALIDATED; + else + va->flags &= ~DRM_GPUVA_INVALIDATED; +} + +/** + * drm_gpuva_invalidated() - indicates whether the backing BO of this &drm_gpuva + * is invalidated + * @va: the &drm_gpuva to check + */ +static inline bool drm_gpuva_invalidated(struct drm_gpuva *va) +{ + return va->flags & DRM_GPUVA_INVALIDATED; +} + +/** + * struct drm_gpuvm - DRM GPU VA Manager + * + * The DRM GPU VA Manager keeps track of a GPU's virtual address space by using + * &maple_tree structures. Typically, this structure is embedded in bigger + * driver structures. + * + * Drivers can pass addresses and ranges in an arbitrary unit, e.g. bytes or + * pages. + * + * There should be one manager instance per GPU virtual address space. + */ +struct drm_gpuvm { + /** + * @name: the name of the DRM GPU VA space + */ + const char *name; + + /** + * @mm_start: start of the VA space + */ + u64 mm_start; + + /** + * @mm_range: length of the VA space + */ + u64 mm_range; + + /** + * @rb: structures to track &drm_gpuva entries + */ + struct { + /** + * @tree: the rb-tree to track GPU VA mappings + */ + struct rb_root_cached tree; + + /** + * @list: the &list_head to track GPU VA mappings + */ + struct list_head list; + } rb; + + /** + * @kernel_alloc_node: + * + * &drm_gpuva representing the address space cutout reserved for + * the kernel + */ + struct drm_gpuva kernel_alloc_node; + + /** + * @ops: &drm_gpuvm_ops providing the split/merge steps to drivers + */ + const struct drm_gpuvm_ops *ops; +}; + +void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, + u64 start_offset, u64 range, + u64 reserve_offset, u64 reserve_range, + const struct drm_gpuvm_ops *ops); +void drm_gpuvm_destroy(struct drm_gpuvm *gpuvm); + +bool drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range); + +static inline struct drm_gpuva * +__drm_gpuva_next(struct drm_gpuva *va) +{ + if (va && !list_is_last(&va->rb.entry, &va->vm->rb.list)) + return list_next_entry(va, rb.entry); + + return NULL; +} + +/** + * drm_gpuvm_for_each_va_range() - iterate over a range of &drm_gpuvas + * @va__: &drm_gpuva structure to assign to in each iteration step + * @gpuvm__: &drm_gpuvm to walk over + * @start__: starting offset, the first gpuva will overlap this + * @end__: ending offset, the last gpuva will start before this (but may + * overlap) + * + * This iterator walks over all &drm_gpuvas in the &drm_gpuvm that lie + * between @start__ and @end__. It is implemented similarly to list_for_each(), + * but is using the &drm_gpuvm's internal interval tree to accelerate + * the search for the starting &drm_gpuva, and hence isn't safe against removal + * of elements. It assumes that @end__ is within (or is the upper limit of) the + * &drm_gpuvm. This iterator does not skip over the &drm_gpuvm's + * @kernel_alloc_node. + */ +#define drm_gpuvm_for_each_va_range(va__, gpuvm__, start__, end__) \ + for (va__ = drm_gpuva_find_first((gpuvm__), (start__), (end__) - (start__)); \ + va__ && (va__->va.addr < (end__)); \ + va__ = __drm_gpuva_next(va__)) + +/** + * drm_gpuvm_for_each_va_range_safe() - safely iterate over a range of + * &drm_gpuvas + * @va__: &drm_gpuva to assign to in each iteration step + * @next__: another &drm_gpuva to use as temporary storage + * @gpuvm__: &drm_gpuvm to walk over + * @start__: starting offset, the first gpuva will overlap this + * @end__: ending offset, the last gpuva will start before this (but may + * overlap) + * + * This iterator walks over all &drm_gpuvas in the &drm_gpuvm that lie + * between @start__ and @end__. It is implemented similarly to + * list_for_each_safe(), but is using the &drm_gpuvm's internal interval + * tree to accelerate the search for the starting &drm_gpuva, and hence is safe + * against removal of elements. It assumes that @end__ is within (or is the + * upper limit of) the &drm_gpuvm. This iterator does not skip over the + * &drm_gpuvm's @kernel_alloc_node. + */ +#define drm_gpuvm_for_each_va_range_safe(va__, next__, gpuvm__, start__, end__) \ + for (va__ = drm_gpuva_find_first((gpuvm__), (start__), (end__) - (start__)), \ + next__ = __drm_gpuva_next(va__); \ + va__ && (va__->va.addr < (end__)); \ + va__ = next__, next__ = __drm_gpuva_next(va__)) + +/** + * drm_gpuvm_for_each_va() - iterate over all &drm_gpuvas + * @va__: &drm_gpuva to assign to in each iteration step + * @gpuvm__: &drm_gpuvm to walk over + * + * This iterator walks over all &drm_gpuva structures associated with the given + * &drm_gpuvm. + */ +#define drm_gpuvm_for_each_va(va__, gpuvm__) \ + list_for_each_entry(va__, &(gpuvm__)->rb.list, rb.entry) + +/** + * drm_gpuvm_for_each_va_safe() - safely iterate over all &drm_gpuvas + * @va__: &drm_gpuva to assign to in each iteration step + * @next__: another &drm_gpuva to use as temporary storage + * @gpuvm__: &drm_gpuvm to walk over + * + * This iterator walks over all &drm_gpuva structures associated with the given + * &drm_gpuvm. It is implemented with list_for_each_entry_safe(), and + * hence safe against the removal of elements. + */ +#define drm_gpuvm_for_each_va_safe(va__, next__, gpuvm__) \ + list_for_each_entry_safe(va__, next__, &(gpuvm__)->rb.list, rb.entry) + +/** + * enum drm_gpuva_op_type - GPU VA operation type + * + * Operations to alter the GPU VA mappings tracked by the &drm_gpuvm. + */ +enum drm_gpuva_op_type { + /** + * @DRM_GPUVA_OP_MAP: the map op type + */ + DRM_GPUVA_OP_MAP, + + /** + * @DRM_GPUVA_OP_REMAP: the remap op type + */ + DRM_GPUVA_OP_REMAP, + + /** + * @DRM_GPUVA_OP_UNMAP: the unmap op type + */ + DRM_GPUVA_OP_UNMAP, + + /** + * @DRM_GPUVA_OP_PREFETCH: the prefetch op type + */ + DRM_GPUVA_OP_PREFETCH, +}; + +/** + * struct drm_gpuva_op_map - GPU VA map operation + * + * This structure represents a single map operation generated by the + * DRM GPU VA manager. + */ +struct drm_gpuva_op_map { + /** + * @va: structure containing address and range of a map + * operation + */ + struct { + /** + * @addr: the base address of the new mapping + */ + u64 addr; + + /** + * @range: the range of the new mapping + */ + u64 range; + } va; + + /** + * @gem: structure containing the &drm_gem_object and it's offset + */ + struct { + /** + * @offset: the offset within the &drm_gem_object + */ + u64 offset; + + /** + * @obj: the &drm_gem_object to map + */ + struct drm_gem_object *obj; + } gem; +}; + +/** + * struct drm_gpuva_op_unmap - GPU VA unmap operation + * + * This structure represents a single unmap operation generated by the + * DRM GPU VA manager. + */ +struct drm_gpuva_op_unmap { + /** + * @va: the &drm_gpuva to unmap + */ + struct drm_gpuva *va; + + /** + * @keep: + * + * Indicates whether this &drm_gpuva is physically contiguous with the + * original mapping request. + * + * Optionally, if &keep is set, drivers may keep the actual page table + * mappings for this &drm_gpuva, adding the missing page table entries + * only and update the &drm_gpuvm accordingly. + */ + bool keep; +}; + +/** + * struct drm_gpuva_op_remap - GPU VA remap operation + * + * This represents a single remap operation generated by the DRM GPU VA manager. + * + * A remap operation is generated when an existing GPU VA mmapping is split up + * by inserting a new GPU VA mapping or by partially unmapping existent + * mapping(s), hence it consists of a maximum of two map and one unmap + * operation. + * + * The @unmap operation takes care of removing the original existing mapping. + * @prev is used to remap the preceding part, @next the subsequent part. + * + * If either a new mapping's start address is aligned with the start address + * of the old mapping or the new mapping's end address is aligned with the + * end address of the old mapping, either @prev or @next is NULL. + * + * Note, the reason for a dedicated remap operation, rather than arbitrary + * unmap and map operations, is to give drivers the chance of extracting driver + * specific data for creating the new mappings from the unmap operations's + * &drm_gpuva structure which typically is embedded in larger driver specific + * structures. + */ +struct drm_gpuva_op_remap { + /** + * @prev: the preceding part of a split mapping + */ + struct drm_gpuva_op_map *prev; + + /** + * @next: the subsequent part of a split mapping + */ + struct drm_gpuva_op_map *next; + + /** + * @unmap: the unmap operation for the original existing mapping + */ + struct drm_gpuva_op_unmap *unmap; +}; + +/** + * struct drm_gpuva_op_prefetch - GPU VA prefetch operation + * + * This structure represents a single prefetch operation generated by the + * DRM GPU VA manager. + */ +struct drm_gpuva_op_prefetch { + /** + * @va: the &drm_gpuva to prefetch + */ + struct drm_gpuva *va; +}; + +/** + * struct drm_gpuva_op - GPU VA operation + * + * This structure represents a single generic operation. + * + * The particular type of the operation is defined by @op. + */ +struct drm_gpuva_op { + /** + * @entry: + * + * The &list_head used to distribute instances of this struct within + * &drm_gpuva_ops. + */ + struct list_head entry; + + /** + * @op: the type of the operation + */ + enum drm_gpuva_op_type op; + + union { + /** + * @map: the map operation + */ + struct drm_gpuva_op_map map; + + /** + * @remap: the remap operation + */ + struct drm_gpuva_op_remap remap; + + /** + * @unmap: the unmap operation + */ + struct drm_gpuva_op_unmap unmap; + + /** + * @prefetch: the prefetch operation + */ + struct drm_gpuva_op_prefetch prefetch; + }; +}; + +/** + * struct drm_gpuva_ops - wraps a list of &drm_gpuva_op + */ +struct drm_gpuva_ops { + /** + * @list: the &list_head + */ + struct list_head list; +}; + +/** + * drm_gpuva_for_each_op() - iterator to walk over &drm_gpuva_ops + * @op: &drm_gpuva_op to assign in each iteration step + * @ops: &drm_gpuva_ops to walk + * + * This iterator walks over all ops within a given list of operations. + */ +#define drm_gpuva_for_each_op(op, ops) list_for_each_entry(op, &(ops)->list, entry) + +/** + * drm_gpuva_for_each_op_safe() - iterator to safely walk over &drm_gpuva_ops + * @op: &drm_gpuva_op to assign in each iteration step + * @next: &next &drm_gpuva_op to store the next step + * @ops: &drm_gpuva_ops to walk + * + * This iterator walks over all ops within a given list of operations. It is + * implemented with list_for_each_safe(), so save against removal of elements. + */ +#define drm_gpuva_for_each_op_safe(op, next, ops) \ + list_for_each_entry_safe(op, next, &(ops)->list, entry) + +/** + * drm_gpuva_for_each_op_from_reverse() - iterate backwards from the given point + * @op: &drm_gpuva_op to assign in each iteration step + * @ops: &drm_gpuva_ops to walk + * + * This iterator walks over all ops within a given list of operations beginning + * from the given operation in reverse order. + */ +#define drm_gpuva_for_each_op_from_reverse(op, ops) \ + list_for_each_entry_from_reverse(op, &(ops)->list, entry) + +/** + * drm_gpuva_first_op() - returns the first &drm_gpuva_op from &drm_gpuva_ops + * @ops: the &drm_gpuva_ops to get the fist &drm_gpuva_op from + */ +#define drm_gpuva_first_op(ops) \ + list_first_entry(&(ops)->list, struct drm_gpuva_op, entry) + +/** + * drm_gpuva_last_op() - returns the last &drm_gpuva_op from &drm_gpuva_ops + * @ops: the &drm_gpuva_ops to get the last &drm_gpuva_op from + */ +#define drm_gpuva_last_op(ops) \ + list_last_entry(&(ops)->list, struct drm_gpuva_op, entry) + +/** + * drm_gpuva_prev_op() - previous &drm_gpuva_op in the list + * @op: the current &drm_gpuva_op + */ +#define drm_gpuva_prev_op(op) list_prev_entry(op, entry) + +/** + * drm_gpuva_next_op() - next &drm_gpuva_op in the list + * @op: the current &drm_gpuva_op + */ +#define drm_gpuva_next_op(op) list_next_entry(op, entry) + +struct drm_gpuva_ops * +drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm, + u64 addr, u64 range, + struct drm_gem_object *obj, u64 offset); +struct drm_gpuva_ops * +drm_gpuvm_sm_unmap_ops_create(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); + +struct drm_gpuva_ops * +drm_gpuvm_prefetch_ops_create(struct drm_gpuvm *gpuvm, + u64 addr, u64 range); + +struct drm_gpuva_ops * +drm_gpuvm_gem_unmap_ops_create(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj); + +void drm_gpuva_ops_free(struct drm_gpuvm *gpuvm, + struct drm_gpuva_ops *ops); + +static inline void drm_gpuva_init_from_op(struct drm_gpuva *va, + struct drm_gpuva_op_map *op) +{ + drm_gpuva_init(va, op->va.addr, op->va.range, + op->gem.obj, op->gem.offset); +} + +/** + * struct drm_gpuvm_ops - callbacks for split/merge steps + * + * This structure defines the callbacks used by &drm_gpuvm_sm_map and + * &drm_gpuvm_sm_unmap to provide the split/merge steps for map and unmap + * operations to drivers. + */ +struct drm_gpuvm_ops { + /** + * @op_alloc: called when the &drm_gpuvm allocates + * a struct drm_gpuva_op + * + * Some drivers may want to embed struct drm_gpuva_op into driver + * specific structures. By implementing this callback drivers can + * allocate memory accordingly. + * + * This callback is optional. + */ + struct drm_gpuva_op *(*op_alloc)(void); + + /** + * @op_free: called when the &drm_gpuvm frees a + * struct drm_gpuva_op + * + * Some drivers may want to embed struct drm_gpuva_op into driver + * specific structures. By implementing this callback drivers can + * free the previously allocated memory accordingly. + * + * This callback is optional. + */ + void (*op_free)(struct drm_gpuva_op *op); + + /** + * @sm_step_map: called from &drm_gpuvm_sm_map to finally insert the + * mapping once all previous steps were completed + * + * The &priv pointer matches the one the driver passed to + * &drm_gpuvm_sm_map or &drm_gpuvm_sm_unmap, respectively. + * + * Can be NULL if &drm_gpuvm_sm_map is used. + */ + int (*sm_step_map)(struct drm_gpuva_op *op, void *priv); + + /** + * @sm_step_remap: called from &drm_gpuvm_sm_map and + * &drm_gpuvm_sm_unmap to split up an existent mapping + * + * This callback is called when existent mapping needs to be split up. + * This is the case when either a newly requested mapping overlaps or + * is enclosed by an existent mapping or a partial unmap of an existent + * mapping is requested. + * + * The &priv pointer matches the one the driver passed to + * &drm_gpuvm_sm_map or &drm_gpuvm_sm_unmap, respectively. + * + * Can be NULL if neither &drm_gpuvm_sm_map nor &drm_gpuvm_sm_unmap is + * used. + */ + int (*sm_step_remap)(struct drm_gpuva_op *op, void *priv); + + /** + * @sm_step_unmap: called from &drm_gpuvm_sm_map and + * &drm_gpuvm_sm_unmap to unmap an existent mapping + * + * This callback is called when existent mapping needs to be unmapped. + * This is the case when either a newly requested mapping encloses an + * existent mapping or an unmap of an existent mapping is requested. + * + * The &priv pointer matches the one the driver passed to + * &drm_gpuvm_sm_map or &drm_gpuvm_sm_unmap, respectively. + * + * Can be NULL if neither &drm_gpuvm_sm_map nor &drm_gpuvm_sm_unmap is + * used. + */ + int (*sm_step_unmap)(struct drm_gpuva_op *op, void *priv); +}; + +int drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv, + u64 addr, u64 range, + struct drm_gem_object *obj, u64 offset); + +int drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv, + u64 addr, u64 range); + +void drm_gpuva_map(struct drm_gpuvm *gpuvm, + struct drm_gpuva *va, + struct drm_gpuva_op_map *op); + +void drm_gpuva_remap(struct drm_gpuva *prev, + struct drm_gpuva *next, + struct drm_gpuva_op_remap *op); + +void drm_gpuva_unmap(struct drm_gpuva_op_unmap *op); + +#endif /* __DRM_GPUVM_H__ */ -- cgit