Skip to content
Snippets Groups Projects
Select Git revision
  • 1f1626ac0428820f998245478610f452650bcab5
  • drm-misc-templates default
  • wip/final/kci-gitlab-lava-v1
  • wip/vignesh/kci-lava-gitlab-runner
  • kci-gitlab-igt-v8
  • kci-gitlab-igt-v4
  • drm-misc-fixes-2024-10-02
  • drm-misc-next-2024-09-26
  • drm-misc-fixes-2024-09-26
  • drm-misc-next-2024-09-20
  • drm-misc-fixes-2024-09-12
  • drm-misc-fixes-2024-09-05
  • drm-misc-next-fixes-2024-09-05
  • drm-misc-fixes-2024-08-29
  • drm-misc-next-2024-08-29
  • drm-misc-next-2024-08-22
  • drm-misc-fixes-2024-08-22
  • drm-misc-next-2024-08-16
  • drm-misc-fixes-2024-08-15
  • drm-misc-next-2024-08-09
  • drm-misc-fixes-2024-08-08
  • drm-misc-next-2024-08-01
  • drm-misc-fixes-2024-08-01
  • drm-misc-next-fixes-2024-07-25
  • drm-misc-next-fixes-2024-07-19
  • drm-misc-next-fixes-2024-07-11
26 results

ttm_device.c

Blame
    • Fedor Pchelkin's avatar
      1f1626ac
      drm/ttm: fix ttm pool initialization for no-dma-device drivers · 1f1626ac
      Fedor Pchelkin authored
      
      QXL driver doesn't use any device for DMA mappings or allocations so
      dev_to_node() will panic inside ttm_device_init() on NUMA systems:
      
      general protection fault, probably for non-canonical address 0xdffffc000000007a: 0000 [#1] PREEMPT SMP KASAN NOPTI
      KASAN: null-ptr-deref in range [0x00000000000003d0-0x00000000000003d7]
      CPU: 1 PID: 1 Comm: swapper/0 Not tainted 6.7.0+ #9
      Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014
      RIP: 0010:ttm_device_init+0x10e/0x340
      Call Trace:
       <TASK>
       qxl_ttm_init+0xaa/0x310
       qxl_device_init+0x1071/0x2000
       qxl_pci_probe+0x167/0x3f0
       local_pci_probe+0xe1/0x1b0
       pci_device_probe+0x29d/0x790
       really_probe+0x251/0x910
       __driver_probe_device+0x1ea/0x390
       driver_probe_device+0x4e/0x2e0
       __driver_attach+0x1e3/0x600
       bus_for_each_dev+0x12d/0x1c0
       bus_add_driver+0x25a/0x590
       driver_register+0x15c/0x4b0
       qxl_pci_driver_init+0x67/0x80
       do_one_initcall+0xf5/0x5d0
       kernel_init_freeable+0x637/0xb10
       kernel_init+0x1c/0x2e0
       ret_from_fork+0x48/0x80
       ret_from_fork_asm+0x1b/0x30
       </TASK>
      Modules linked in:
      ---[ end trace 0000000000000000 ]---
      RIP: 0010:ttm_device_init+0x10e/0x340
      
      Fall back to NUMA_NO_NODE if there is no device for DMA.
      
      Found by Linux Verification Center (linuxtesting.org).
      
      Fixes: b0a7ce53 ("drm/ttm: Schedule delayed_delete worker closer")
      Signed-off-by: default avatarFedor Pchelkin <pchelkin@ispras.ru>
      Link: https://patchwork.freedesktop.org/patch/msgid/20240113213347.9562-1-pchelkin@ispras.ru
      
      
      Signed-off-by: default avatarChristian König <christian.koenig@amd.com>
      1f1626ac
      History
      drm/ttm: fix ttm pool initialization for no-dma-device drivers
      Fedor Pchelkin authored
      
      QXL driver doesn't use any device for DMA mappings or allocations so
      dev_to_node() will panic inside ttm_device_init() on NUMA systems:
      
      general protection fault, probably for non-canonical address 0xdffffc000000007a: 0000 [#1] PREEMPT SMP KASAN NOPTI
      KASAN: null-ptr-deref in range [0x00000000000003d0-0x00000000000003d7]
      CPU: 1 PID: 1 Comm: swapper/0 Not tainted 6.7.0+ #9
      Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014
      RIP: 0010:ttm_device_init+0x10e/0x340
      Call Trace:
       <TASK>
       qxl_ttm_init+0xaa/0x310
       qxl_device_init+0x1071/0x2000
       qxl_pci_probe+0x167/0x3f0
       local_pci_probe+0xe1/0x1b0
       pci_device_probe+0x29d/0x790
       really_probe+0x251/0x910
       __driver_probe_device+0x1ea/0x390
       driver_probe_device+0x4e/0x2e0
       __driver_attach+0x1e3/0x600
       bus_for_each_dev+0x12d/0x1c0
       bus_add_driver+0x25a/0x590
       driver_register+0x15c/0x4b0
       qxl_pci_driver_init+0x67/0x80
       do_one_initcall+0xf5/0x5d0
       kernel_init_freeable+0x637/0xb10
       kernel_init+0x1c/0x2e0
       ret_from_fork+0x48/0x80
       ret_from_fork_asm+0x1b/0x30
       </TASK>
      Modules linked in:
      ---[ end trace 0000000000000000 ]---
      RIP: 0010:ttm_device_init+0x10e/0x340
      
      Fall back to NUMA_NO_NODE if there is no device for DMA.
      
      Found by Linux Verification Center (linuxtesting.org).
      
      Fixes: b0a7ce53 ("drm/ttm: Schedule delayed_delete worker closer")
      Signed-off-by: default avatarFedor Pchelkin <pchelkin@ispras.ru>
      Link: https://patchwork.freedesktop.org/patch/msgid/20240113213347.9562-1-pchelkin@ispras.ru
      
      
      Signed-off-by: default avatarChristian König <christian.koenig@amd.com>
    ttm_device.c 7.77 KiB
    /* SPDX-License-Identifier: GPL-2.0 OR MIT */
    
    /*
     * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
     * Copyright 2020 Advanced Micro Devices, Inc.
     *
     * 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.
     *
     * Authors: Christian König
     */
    
    #define pr_fmt(fmt) "[TTM DEVICE] " fmt
    
    #include <linux/mm.h>
    
    #include <drm/ttm/ttm_bo.h>
    #include <drm/ttm/ttm_device.h>
    #include <drm/ttm/ttm_tt.h>
    #include <drm/ttm/ttm_placement.h>
    
    #include "ttm_module.h"
    
    /*
     * ttm_global_mutex - protecting the global state
     */
    static DEFINE_MUTEX(ttm_global_mutex);
    static unsigned ttm_glob_use_count;
    struct ttm_global ttm_glob;
    EXPORT_SYMBOL(ttm_glob);
    
    struct dentry *ttm_debugfs_root;
    
    static void ttm_global_release(void)
    {
    	struct ttm_global *glob = &ttm_glob;
    
    	mutex_lock(&ttm_global_mutex);
    	if (--ttm_glob_use_count > 0)
    		goto out;
    
    	ttm_pool_mgr_fini();
    	debugfs_remove(ttm_debugfs_root);
    
    	__free_page(glob->dummy_read_page);
    	memset(glob, 0, sizeof(*glob));
    out:
    	mutex_unlock(&ttm_global_mutex);
    }
    
    static int ttm_global_init(void)
    {
    	struct ttm_global *glob = &ttm_glob;
    	unsigned long num_pages, num_dma32;
    	struct sysinfo si;
    	int ret = 0;
    
    	mutex_lock(&ttm_global_mutex);
    	if (++ttm_glob_use_count > 1)
    		goto out;
    
    	si_meminfo(&si);
    
    	ttm_debugfs_root = debugfs_create_dir("ttm", NULL);
    	if (IS_ERR(ttm_debugfs_root)) {
    		ttm_debugfs_root = NULL;
    	}
    
    	/* Limit the number of pages in the pool to about 50% of the total
    	 * system memory.
    	 */
    	num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT;
    	num_pages /= 2;
    
    	/* But for DMA32 we limit ourself to only use 2GiB maximum. */
    	num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit
    		>> PAGE_SHIFT;
    	num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT));
    
    	ttm_pool_mgr_init(num_pages);
    	ttm_tt_mgr_init(num_pages, num_dma32);
    
    	glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32);
    
    	if (unlikely(glob->dummy_read_page == NULL)) {
    		ret = -ENOMEM;
    		goto out;
    	}
    
    	INIT_LIST_HEAD(&glob->device_list);
    	atomic_set(&glob->bo_count, 0);
    
    	debugfs_create_atomic_t("buffer_objects", 0444, ttm_debugfs_root,
    				&glob->bo_count);
    out:
    	if (ret && ttm_debugfs_root)
    		debugfs_remove(ttm_debugfs_root);
    	if (ret)
    		--ttm_glob_use_count;
    	mutex_unlock(&ttm_global_mutex);
    	return ret;
    }
    
    /*
     * A buffer object shrink method that tries to swap out the first
     * buffer object on the global::swap_lru list.
     */
    int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags)
    {
    	struct ttm_global *glob = &ttm_glob;
    	struct ttm_device *bdev;
    	int ret = 0;
    
    	mutex_lock(&ttm_global_mutex);
    	list_for_each_entry(bdev, &glob->device_list, device_list) {
    		ret = ttm_device_swapout(bdev, ctx, gfp_flags);
    		if (ret > 0) {
    			list_move_tail(&bdev->device_list, &glob->device_list);
    			break;
    		}
    	}
    	mutex_unlock(&ttm_global_mutex);
    	return ret;
    }
    
    int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
    		       gfp_t gfp_flags)
    {
    	struct ttm_resource_cursor cursor;
    	struct ttm_resource_manager *man;
    	struct ttm_resource *res;
    	unsigned i;
    	int ret;
    
    	spin_lock(&bdev->lru_lock);
    	for (i = TTM_PL_SYSTEM; i < TTM_NUM_MEM_TYPES; ++i) {
    		man = ttm_manager_type(bdev, i);
    		if (!man || !man->use_tt)
    			continue;
    
    		ttm_resource_manager_for_each_res(man, &cursor, res) {
    			struct ttm_buffer_object *bo = res->bo;
    			uint32_t num_pages;
    
    			if (!bo || bo->resource != res)
    				continue;
    
    			num_pages = PFN_UP(bo->base.size);
    			ret = ttm_bo_swapout(bo, ctx, gfp_flags);
    			/* ttm_bo_swapout has dropped the lru_lock */
    			if (!ret)
    				return num_pages;
    			if (ret != -EBUSY)
    				return ret;
    		}
    	}
    	spin_unlock(&bdev->lru_lock);
    	return 0;
    }
    EXPORT_SYMBOL(ttm_device_swapout);
    
    /**
     * ttm_device_init
     *
     * @bdev: A pointer to a struct ttm_device to initialize.
     * @funcs: Function table for the device.
     * @dev: The core kernel device pointer for DMA mappings and allocations.
     * @mapping: The address space to use for this bo.
     * @vma_manager: A pointer to a vma manager.
     * @use_dma_alloc: If coherent DMA allocation API should be used.
     * @use_dma32: If we should use GFP_DMA32 for device memory allocations.
     *
     * Initializes a struct ttm_device:
     * Returns:
     * !0: Failure.
     */
    int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *funcs,
    		    struct device *dev, struct address_space *mapping,
    		    struct drm_vma_offset_manager *vma_manager,
    		    bool use_dma_alloc, bool use_dma32)
    {
    	struct ttm_global *glob = &ttm_glob;
    	int ret, nid;
    
    	if (WARN_ON(vma_manager == NULL))
    		return -EINVAL;
    
    	ret = ttm_global_init();
    	if (ret)
    		return ret;
    
    	bdev->wq = alloc_workqueue("ttm",
    				   WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_UNBOUND, 16);
    	if (!bdev->wq) {
    		ttm_global_release();
    		return -ENOMEM;
    	}
    
    	bdev->funcs = funcs;
    
    	ttm_sys_man_init(bdev);
    
    	if (dev)
    		nid = dev_to_node(dev);
    	else
    		nid = NUMA_NO_NODE;
    
    	ttm_pool_init(&bdev->pool, dev, nid, use_dma_alloc, use_dma32);
    
    	bdev->vma_manager = vma_manager;
    	spin_lock_init(&bdev->lru_lock);
    	INIT_LIST_HEAD(&bdev->pinned);
    	bdev->dev_mapping = mapping;
    	mutex_lock(&ttm_global_mutex);
    	list_add_tail(&bdev->device_list, &glob->device_list);
    	mutex_unlock(&ttm_global_mutex);
    
    	return 0;
    }
    EXPORT_SYMBOL(ttm_device_init);
    
    void ttm_device_fini(struct ttm_device *bdev)
    {
    	struct ttm_resource_manager *man;
    	unsigned i;
    
    	mutex_lock(&ttm_global_mutex);
    	list_del(&bdev->device_list);
    	mutex_unlock(&ttm_global_mutex);
    
    	drain_workqueue(bdev->wq);
    	destroy_workqueue(bdev->wq);
    
    	man = ttm_manager_type(bdev, TTM_PL_SYSTEM);
    	ttm_resource_manager_set_used(man, false);
    	ttm_set_driver_manager(bdev, TTM_PL_SYSTEM, NULL);
    
    	spin_lock(&bdev->lru_lock);
    	for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
    		if (list_empty(&man->lru[0]))
    			pr_debug("Swap list %d was clean\n", i);
    	spin_unlock(&bdev->lru_lock);
    
    	ttm_pool_fini(&bdev->pool);
    	ttm_global_release();
    }
    EXPORT_SYMBOL(ttm_device_fini);
    
    static void ttm_device_clear_lru_dma_mappings(struct ttm_device *bdev,
    					      struct list_head *list)
    {
    	struct ttm_resource *res;
    
    	spin_lock(&bdev->lru_lock);
    	while ((res = list_first_entry_or_null(list, typeof(*res), lru))) {
    		struct ttm_buffer_object *bo = res->bo;
    
    		/* Take ref against racing releases once lru_lock is unlocked */
    		if (!ttm_bo_get_unless_zero(bo))
    			continue;
    
    		list_del_init(&res->lru);
    		spin_unlock(&bdev->lru_lock);
    
    		if (bo->ttm)
    			ttm_tt_unpopulate(bo->bdev, bo->ttm);
    
    		ttm_bo_put(bo);
    		spin_lock(&bdev->lru_lock);
    	}
    	spin_unlock(&bdev->lru_lock);
    }
    
    void ttm_device_clear_dma_mappings(struct ttm_device *bdev)
    {
    	struct ttm_resource_manager *man;
    	unsigned int i, j;
    
    	ttm_device_clear_lru_dma_mappings(bdev, &bdev->pinned);
    
    	for (i = TTM_PL_SYSTEM; i < TTM_NUM_MEM_TYPES; ++i) {
    		man = ttm_manager_type(bdev, i);
    		if (!man || !man->use_tt)
    			continue;
    
    		for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j)
    			ttm_device_clear_lru_dma_mappings(bdev, &man->lru[j]);
    	}
    }
    EXPORT_SYMBOL(ttm_device_clear_dma_mappings);