Merge tag 'drm-xe-next-2025-07-10' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-next

UAPI Changes:
 - Documentation fixes (Shuicheng)

Cross-subsystem Changes:
 - MTD intel-dg driver for dgfx non-volatile memory device (Sasha)
 - i2c: designware changes to allow i2c integration with BMG (Heikki)

Core Changes:
 - Restructure migration in preparation for multi-device (Brost, Thomas)
 - Expose fan control and voltage regulator version on sysfs (Raag)

Driver Changes:
 - Add WildCat Lake support (Roper)
 - Add aux bus child device driver for NVM on DGFX (Sasha)
 - Some refactor and fixes to allow cleaner BMG w/a (Lucas, Maarten, Auld)
 - BMG w/a (Vinay)
 - Improve handling of aborted probe (Michal)
 - Do not wedge device on killed exec queues (Brost)
 - Init changes for flicker-free boot (Maarten)
 - Fix out-of-bounds field write in MI_STORE_DATA_IMM (Jia)
 - Enable the GuC Dynamic Inhibit Context Switch optimization (Daniele)
 - Drop bo->size (Brost)
 - Builds and KConfig fixes (Harry, Maarten)
 - Consolidate LRC offset calculations (Tvrtko)
 - Fix potential leak in hw_engine_group (Michal)
 - Future-proof for multi-tile + multi-GT cases (Roper)
 - Validate gt in pmu event (Riana)
 - SRIOV PF: Clear all LMTT pages on alloc (Michal)
 - Allocate PF queue size on pow2 boundary (Brost)
 - SRIOV VF: Make multi-GT migration less error prone (Tomasz)
 - Revert indirect ring state patch to fix random LRC context switches failures (Brost)
 - Fix compressed VRAM handling (Auld)
 - Add one additional BMG PCI ID (Ravi)
 - Recommend GuC v70.46.2 for BMG, LNL, DG2 (Julia)
 - Add GuC and HuC to PTL (Daniele)
 - Drop PTL force_probe requirement (Atwood)
 - Fix error flow in display suspend (Shuicheng)
 - Disable GuC communication on hardware initialization error (Zhanjun)
 - Devcoredump fixes and clean up (Shuicheng)
 - SRIOV PF: Downgrade some info to debug (Michal)
 - Don't allocate temporary GuC policies object (Michal)
 - Support for I2C attached MCUs (Heikki, Raag, Riana)
 - Add GPU memory bo trace points (Juston)
 - SRIOV VF: Skip some W/a (Michal)
 - Correct comment of xe_pm_set_vram_threshold (Shuicheng)
 - Cancel ongoing H2G requests when stopping CT (Michal)

Signed-off-by: Simona Vetter <simona.vetter@ffwll.ch>
From: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/aHA7184UnWlONORU@intel.com
This commit is contained in:
Simona Vetter
2025-07-11 11:08:53 +02:00
98 changed files with 3981 additions and 1630 deletions
+9 -3
View File
@@ -73,15 +73,21 @@ Overview of baseline design
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Locking
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Migration
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Partial Unmapping of Ranges
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Examples
Overview of drm_pagemap design
==============================
.. kernel-doc:: drivers/gpu/drm/drm_pagemap.c
:doc: Overview
.. kernel-doc:: drivers/gpu/drm/drm_pagemap.c
:doc: Migration
Possible future design features
===============================
+7
View File
@@ -12089,6 +12089,13 @@ L: linux-kernel@vger.kernel.org
S: Supported
F: arch/x86/include/asm/intel-family.h
INTEL DISCRETE GRAPHICS NVM MTD DRIVER
M: Alexander Usyskin <alexander.usyskin@intel.com>
L: linux-mtd@lists.infradead.org
S: Supported
F: drivers/mtd/devices/mtd_intel_dg.c
F: include/linux/intel_dg_nvm_aux.h
INTEL DRM DISPLAY FOR XE AND I915 DRIVERS
M: Jani Nikula <jani.nikula@linux.intel.com>
M: Rodrigo Vivi <rodrigo.vivi@intel.com>
+5 -1
View File
@@ -104,7 +104,11 @@ obj-$(CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS) += drm_panel_backlight_quirks.o
#
obj-$(CONFIG_DRM_EXEC) += drm_exec.o
obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o
obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm.o
drm_gpusvm_helper-y := \
drm_gpusvm.o\
drm_pagemap.o
obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm_helper.o
obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o
+13 -749
View File
@@ -9,10 +9,9 @@
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/hmm.h>
#include <linux/hugetlb_inline.h>
#include <linux/memremap.h>
#include <linux/migrate.h>
#include <linux/mm_types.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <drm/drm_device.h>
@@ -108,21 +107,6 @@
* to add annotations to GPU SVM.
*/
/**
* DOC: Migration
*
* The migration support is quite simple, allowing migration between RAM and
* device memory at the range granularity. For example, GPU SVM currently does
* not support mixing RAM and device memory pages within a range. This means
* that upon GPU fault, the entire range can be migrated to device memory, and
* upon CPU fault, the entire range is migrated to RAM. Mixed RAM and device
* memory storage within a range could be added in the future if required.
*
* The reasoning for only supporting range granularity is as follows: it
* simplifies the implementation, and range sizes are driver-defined and should
* be relatively small.
*/
/**
* DOC: Partial Unmapping of Ranges
*
@@ -192,12 +176,9 @@
* }
*
* if (driver_migration_policy(range)) {
* mmap_read_lock(mm);
* devmem = driver_alloc_devmem();
* err = drm_gpusvm_migrate_to_devmem(gpusvm, range,
* devmem_allocation,
* &ctx);
* mmap_read_unlock(mm);
* err = drm_pagemap_populate_mm(driver_choose_drm_pagemap(),
* gpuva_start, gpuva_end, gpusvm->mm,
* ctx->timeslice_ms);
* if (err) // CPU mappings may have changed
* goto retry;
* }
@@ -289,97 +270,6 @@ npages_in_range(unsigned long start, unsigned long end)
return (end - start) >> PAGE_SHIFT;
}
/**
* struct drm_gpusvm_zdd - GPU SVM zone device data
*
* @refcount: Reference count for the zdd
* @devmem_allocation: device memory allocation
* @device_private_page_owner: Device private pages owner
*
* This structure serves as a generic wrapper installed in
* page->zone_device_data. It provides infrastructure for looking up a device
* memory allocation upon CPU page fault and asynchronously releasing device
* memory once the CPU has no page references. Asynchronous release is useful
* because CPU page references can be dropped in IRQ contexts, while releasing
* device memory likely requires sleeping locks.
*/
struct drm_gpusvm_zdd {
struct kref refcount;
struct drm_gpusvm_devmem *devmem_allocation;
void *device_private_page_owner;
};
/**
* drm_gpusvm_zdd_alloc() - Allocate a zdd structure.
* @device_private_page_owner: Device private pages owner
*
* This function allocates and initializes a new zdd structure. It sets up the
* reference count and initializes the destroy work.
*
* Return: Pointer to the allocated zdd on success, ERR_PTR() on failure.
*/
static struct drm_gpusvm_zdd *
drm_gpusvm_zdd_alloc(void *device_private_page_owner)
{
struct drm_gpusvm_zdd *zdd;
zdd = kmalloc(sizeof(*zdd), GFP_KERNEL);
if (!zdd)
return NULL;
kref_init(&zdd->refcount);
zdd->devmem_allocation = NULL;
zdd->device_private_page_owner = device_private_page_owner;
return zdd;
}
/**
* drm_gpusvm_zdd_get() - Get a reference to a zdd structure.
* @zdd: Pointer to the zdd structure.
*
* This function increments the reference count of the provided zdd structure.
*
* Return: Pointer to the zdd structure.
*/
static struct drm_gpusvm_zdd *drm_gpusvm_zdd_get(struct drm_gpusvm_zdd *zdd)
{
kref_get(&zdd->refcount);
return zdd;
}
/**
* drm_gpusvm_zdd_destroy() - Destroy a zdd structure.
* @ref: Pointer to the reference count structure.
*
* This function queues the destroy_work of the zdd for asynchronous destruction.
*/
static void drm_gpusvm_zdd_destroy(struct kref *ref)
{
struct drm_gpusvm_zdd *zdd =
container_of(ref, struct drm_gpusvm_zdd, refcount);
struct drm_gpusvm_devmem *devmem = zdd->devmem_allocation;
if (devmem) {
complete_all(&devmem->detached);
if (devmem->ops->devmem_release)
devmem->ops->devmem_release(devmem);
}
kfree(zdd);
}
/**
* drm_gpusvm_zdd_put() - Put a zdd reference.
* @zdd: Pointer to the zdd structure.
*
* This function decrements the reference count of the provided zdd structure
* and schedules its destruction if the count drops to zero.
*/
static void drm_gpusvm_zdd_put(struct drm_gpusvm_zdd *zdd)
{
kref_put(&zdd->refcount, drm_gpusvm_zdd_destroy);
}
/**
* drm_gpusvm_range_find() - Find GPU SVM range from GPU SVM notifier
* @notifier: Pointer to the GPU SVM notifier structure.
@@ -946,7 +836,7 @@ retry:
* process-many-malloc' fails. In the failure case, each process
* mallocs 16k but the CPU VMA is ~128k which results in 64k SVM
* ranges. When migrating the SVM ranges, some processes fail in
* drm_gpusvm_migrate_to_devmem with 'migrate.cpages != npages'
* drm_pagemap_migrate_to_devmem with 'migrate.cpages != npages'
* and then upon drm_gpusvm_range_get_pages device pages from
* other processes are collected + faulted in which creates all
* sorts of problems. Unsure exactly how this happening, also
@@ -1364,7 +1254,7 @@ int drm_gpusvm_range_get_pages(struct drm_gpusvm *gpusvm,
.dev_private_owner = gpusvm->device_private_page_owner,
};
struct mm_struct *mm = gpusvm->mm;
struct drm_gpusvm_zdd *zdd;
void *zdd;
unsigned long timeout =
jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
unsigned long i, j;
@@ -1447,6 +1337,7 @@ map_pages:
}
zdd = NULL;
pagemap = NULL;
num_dma_mapped = 0;
for (i = 0, j = 0; i < npages; ++j) {
struct page *page = hmm_pfn_to_page(pfns[i]);
@@ -1466,7 +1357,7 @@ map_pages:
}
pagemap = page_pgmap(page);
dpagemap = zdd->devmem_allocation->dpagemap;
dpagemap = drm_pagemap_page_to_dpagemap(page);
if (drm_WARN_ON(gpusvm->drm, !dpagemap)) {
/*
* Raced. This is not supposed to happen
@@ -1490,7 +1381,7 @@ map_pages:
} else {
dma_addr_t addr;
if (is_zone_device_page(page) || zdd) {
if (is_zone_device_page(page) || pagemap) {
err = -EOPNOTSUPP;
goto err_unmap;
}
@@ -1518,7 +1409,7 @@ map_pages:
flags.has_dma_mapping = true;
}
if (zdd) {
if (pagemap) {
flags.has_devmem_pages = true;
range->dpagemap = dpagemap;
}
@@ -1546,6 +1437,7 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_range_get_pages);
/**
* drm_gpusvm_range_unmap_pages() - Unmap pages associated with a GPU SVM range
* drm_gpusvm_range_evict() - Evict GPU SVM range
* @gpusvm: Pointer to the GPU SVM structure
* @range: Pointer to the GPU SVM range structure
* @ctx: GPU SVM context
@@ -1576,562 +1468,11 @@ void drm_gpusvm_range_unmap_pages(struct drm_gpusvm *gpusvm,
EXPORT_SYMBOL_GPL(drm_gpusvm_range_unmap_pages);
/**
* drm_gpusvm_migration_unlock_put_page() - Put a migration page
* @page: Pointer to the page to put
*
* This function unlocks and puts a page.
*/
static void drm_gpusvm_migration_unlock_put_page(struct page *page)
{
unlock_page(page);
put_page(page);
}
/**
* drm_gpusvm_migration_unlock_put_pages() - Put migration pages
* @npages: Number of pages
* @migrate_pfn: Array of migrate page frame numbers
*
* This function unlocks and puts an array of pages.
*/
static void drm_gpusvm_migration_unlock_put_pages(unsigned long npages,
unsigned long *migrate_pfn)
{
unsigned long i;
for (i = 0; i < npages; ++i) {
struct page *page;
if (!migrate_pfn[i])
continue;
page = migrate_pfn_to_page(migrate_pfn[i]);
drm_gpusvm_migration_unlock_put_page(page);
migrate_pfn[i] = 0;
}
}
/**
* drm_gpusvm_get_devmem_page() - Get a reference to a device memory page
* @page: Pointer to the page
* @zdd: Pointer to the GPU SVM zone device data
*
* This function associates the given page with the specified GPU SVM zone
* device data and initializes it for zone device usage.
*/
static void drm_gpusvm_get_devmem_page(struct page *page,
struct drm_gpusvm_zdd *zdd)
{
page->zone_device_data = drm_gpusvm_zdd_get(zdd);
zone_device_page_init(page);
}
/**
* drm_gpusvm_migrate_map_pages() - Map migration pages for GPU SVM migration
* @dev: The device for which the pages are being mapped
* @dma_addr: Array to store DMA addresses corresponding to mapped pages
* @migrate_pfn: Array of migrate page frame numbers to map
* @npages: Number of pages to map
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
*
* This function maps pages of memory for migration usage in GPU SVM. It
* iterates over each page frame number provided in @migrate_pfn, maps the
* corresponding page, and stores the DMA address in the provided @dma_addr
* array.
*
* Return: 0 on success, -EFAULT if an error occurs during mapping.
*/
static int drm_gpusvm_migrate_map_pages(struct device *dev,
dma_addr_t *dma_addr,
unsigned long *migrate_pfn,
unsigned long npages,
enum dma_data_direction dir)
{
unsigned long i;
for (i = 0; i < npages; ++i) {
struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
if (!page)
continue;
if (WARN_ON_ONCE(is_zone_device_page(page)))
return -EFAULT;
dma_addr[i] = dma_map_page(dev, page, 0, PAGE_SIZE, dir);
if (dma_mapping_error(dev, dma_addr[i]))
return -EFAULT;
}
return 0;
}
/**
* drm_gpusvm_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration
* @dev: The device for which the pages were mapped
* @dma_addr: Array of DMA addresses corresponding to mapped pages
* @npages: Number of pages to unmap
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
*
* This function unmaps previously mapped pages of memory for GPU Shared Virtual
* Memory (SVM). It iterates over each DMA address provided in @dma_addr, checks
* if it's valid and not already unmapped, and unmaps the corresponding page.
*/
static void drm_gpusvm_migrate_unmap_pages(struct device *dev,
dma_addr_t *dma_addr,
unsigned long npages,
enum dma_data_direction dir)
{
unsigned long i;
for (i = 0; i < npages; ++i) {
if (!dma_addr[i] || dma_mapping_error(dev, dma_addr[i]))
continue;
dma_unmap_page(dev, dma_addr[i], PAGE_SIZE, dir);
}
}
/**
* drm_gpusvm_migrate_to_devmem() - Migrate GPU SVM range to device memory
* drm_gpusvm_range_evict() - Evict GPU SVM range
* @gpusvm: Pointer to the GPU SVM structure
* @range: Pointer to the GPU SVM range structure
* @devmem_allocation: Pointer to the device memory allocation. The caller
* should hold a reference to the device memory allocation,
* which should be dropped via ops->devmem_release or upon
* the failure of this function.
* @ctx: GPU SVM context
*
* This function migrates the specified GPU SVM range to device memory. It
* performs the necessary setup and invokes the driver-specific operations for
* migration to device memory. Upon successful return, @devmem_allocation can
* safely reference @range until ops->devmem_release is called which only upon
* successful return. Expected to be called while holding the mmap lock in read
* mode.
*
* Return: 0 on success, negative error code on failure.
*/
int drm_gpusvm_migrate_to_devmem(struct drm_gpusvm *gpusvm,
struct drm_gpusvm_range *range,
struct drm_gpusvm_devmem *devmem_allocation,
const struct drm_gpusvm_ctx *ctx)
{
const struct drm_gpusvm_devmem_ops *ops = devmem_allocation->ops;
unsigned long start = drm_gpusvm_range_start(range),
end = drm_gpusvm_range_end(range);
struct migrate_vma migrate = {
.start = start,
.end = end,
.pgmap_owner = gpusvm->device_private_page_owner,
.flags = MIGRATE_VMA_SELECT_SYSTEM,
};
struct mm_struct *mm = gpusvm->mm;
unsigned long i, npages = npages_in_range(start, end);
struct vm_area_struct *vas;
struct drm_gpusvm_zdd *zdd = NULL;
struct page **pages;
dma_addr_t *dma_addr;
void *buf;
int err;
mmap_assert_locked(gpusvm->mm);
if (!range->flags.migrate_devmem)
return -EINVAL;
if (!ops->populate_devmem_pfn || !ops->copy_to_devmem ||
!ops->copy_to_ram)
return -EOPNOTSUPP;
vas = vma_lookup(mm, start);
if (!vas) {
err = -ENOENT;
goto err_out;
}
if (end > vas->vm_end || start < vas->vm_start) {
err = -EINVAL;
goto err_out;
}
if (!vma_is_anonymous(vas)) {
err = -EBUSY;
goto err_out;
}
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
sizeof(*pages), GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto err_out;
}
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
zdd = drm_gpusvm_zdd_alloc(gpusvm->device_private_page_owner);
if (!zdd) {
err = -ENOMEM;
goto err_free;
}
migrate.vma = vas;
migrate.src = buf;
migrate.dst = migrate.src + npages;
err = migrate_vma_setup(&migrate);
if (err)
goto err_free;
if (!migrate.cpages) {
err = -EFAULT;
goto err_free;
}
if (migrate.cpages != npages) {
err = -EBUSY;
goto err_finalize;
}
err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst);
if (err)
goto err_finalize;
err = drm_gpusvm_migrate_map_pages(devmem_allocation->dev, dma_addr,
migrate.src, npages, DMA_TO_DEVICE);
if (err)
goto err_finalize;
for (i = 0; i < npages; ++i) {
struct page *page = pfn_to_page(migrate.dst[i]);
pages[i] = page;
migrate.dst[i] = migrate_pfn(migrate.dst[i]);
drm_gpusvm_get_devmem_page(page, zdd);
}
err = ops->copy_to_devmem(pages, dma_addr, npages);
if (err)
goto err_finalize;
/* Upon success bind devmem allocation to range and zdd */
devmem_allocation->timeslice_expiration = get_jiffies_64() +
msecs_to_jiffies(ctx->timeslice_ms);
zdd->devmem_allocation = devmem_allocation; /* Owns ref */
err_finalize:
if (err)
drm_gpusvm_migration_unlock_put_pages(npages, migrate.dst);
migrate_vma_pages(&migrate);
migrate_vma_finalize(&migrate);
drm_gpusvm_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
DMA_TO_DEVICE);
err_free:
if (zdd)
drm_gpusvm_zdd_put(zdd);
kvfree(buf);
err_out:
return err;
}
EXPORT_SYMBOL_GPL(drm_gpusvm_migrate_to_devmem);
/**
* drm_gpusvm_migrate_populate_ram_pfn() - Populate RAM PFNs for a VM area
* @vas: Pointer to the VM area structure, can be NULL
* @fault_page: Fault page
* @npages: Number of pages to populate
* @mpages: Number of pages to migrate
* @src_mpfn: Source array of migrate PFNs
* @mpfn: Array of migrate PFNs to populate
* @addr: Start address for PFN allocation
*
* This function populates the RAM migrate page frame numbers (PFNs) for the
* specified VM area structure. It allocates and locks pages in the VM area for
* RAM usage. If vas is non-NULL use alloc_page_vma for allocation, if NULL use
* alloc_page for allocation.
*
* Return: 0 on success, negative error code on failure.
*/
static int drm_gpusvm_migrate_populate_ram_pfn(struct vm_area_struct *vas,
struct page *fault_page,
unsigned long npages,
unsigned long *mpages,
unsigned long *src_mpfn,
unsigned long *mpfn,
unsigned long addr)
{
unsigned long i;
for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
struct page *page, *src_page;
if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE))
continue;
src_page = migrate_pfn_to_page(src_mpfn[i]);
if (!src_page)
continue;
if (fault_page) {
if (src_page->zone_device_data !=
fault_page->zone_device_data)
continue;
}
if (vas)
page = alloc_page_vma(GFP_HIGHUSER, vas, addr);
else
page = alloc_page(GFP_HIGHUSER);
if (!page)
goto free_pages;
mpfn[i] = migrate_pfn(page_to_pfn(page));
}
for (i = 0; i < npages; ++i) {
struct page *page = migrate_pfn_to_page(mpfn[i]);
if (!page)
continue;
WARN_ON_ONCE(!trylock_page(page));
++*mpages;
}
return 0;
free_pages:
for (i = 0; i < npages; ++i) {
struct page *page = migrate_pfn_to_page(mpfn[i]);
if (!page)
continue;
put_page(page);
mpfn[i] = 0;
}
return -ENOMEM;
}
/**
* drm_gpusvm_evict_to_ram() - Evict GPU SVM range to RAM
* @devmem_allocation: Pointer to the device memory allocation
*
* Similar to __drm_gpusvm_migrate_to_ram but does not require mmap lock and
* migration done via migrate_device_* functions.
*
* Return: 0 on success, negative error code on failure.
*/
int drm_gpusvm_evict_to_ram(struct drm_gpusvm_devmem *devmem_allocation)
{
const struct drm_gpusvm_devmem_ops *ops = devmem_allocation->ops;
unsigned long npages, mpages = 0;
struct page **pages;
unsigned long *src, *dst;
dma_addr_t *dma_addr;
void *buf;
int i, err = 0;
unsigned int retry_count = 2;
npages = devmem_allocation->size >> PAGE_SHIFT;
retry:
if (!mmget_not_zero(devmem_allocation->mm))
return -EFAULT;
buf = kvcalloc(npages, 2 * sizeof(*src) + sizeof(*dma_addr) +
sizeof(*pages), GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto err_out;
}
src = buf;
dst = buf + (sizeof(*src) * npages);
dma_addr = buf + (2 * sizeof(*src) * npages);
pages = buf + (2 * sizeof(*src) + sizeof(*dma_addr)) * npages;
err = ops->populate_devmem_pfn(devmem_allocation, npages, src);
if (err)
goto err_free;
err = migrate_device_pfns(src, npages);
if (err)
goto err_free;
err = drm_gpusvm_migrate_populate_ram_pfn(NULL, NULL, npages, &mpages,
src, dst, 0);
if (err || !mpages)
goto err_finalize;
err = drm_gpusvm_migrate_map_pages(devmem_allocation->dev, dma_addr,
dst, npages, DMA_FROM_DEVICE);
if (err)
goto err_finalize;
for (i = 0; i < npages; ++i)
pages[i] = migrate_pfn_to_page(src[i]);
err = ops->copy_to_ram(pages, dma_addr, npages);
if (err)
goto err_finalize;
err_finalize:
if (err)
drm_gpusvm_migration_unlock_put_pages(npages, dst);
migrate_device_pages(src, dst, npages);
migrate_device_finalize(src, dst, npages);
drm_gpusvm_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
DMA_FROM_DEVICE);
err_free:
kvfree(buf);
err_out:
mmput_async(devmem_allocation->mm);
if (completion_done(&devmem_allocation->detached))
return 0;
if (retry_count--) {
cond_resched();
goto retry;
}
return err ?: -EBUSY;
}
EXPORT_SYMBOL_GPL(drm_gpusvm_evict_to_ram);
/**
* __drm_gpusvm_migrate_to_ram() - Migrate GPU SVM range to RAM (internal)
* @vas: Pointer to the VM area structure
* @device_private_page_owner: Device private pages owner
* @page: Pointer to the page for fault handling (can be NULL)
* @fault_addr: Fault address
* @size: Size of migration
*
* This internal function performs the migration of the specified GPU SVM range
* to RAM. It sets up the migration, populates + dma maps RAM PFNs, and
* invokes the driver-specific operations for migration to RAM.
*
* Return: 0 on success, negative error code on failure.
*/
static int __drm_gpusvm_migrate_to_ram(struct vm_area_struct *vas,
void *device_private_page_owner,
struct page *page,
unsigned long fault_addr,
unsigned long size)
{
struct migrate_vma migrate = {
.vma = vas,
.pgmap_owner = device_private_page_owner,
.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE |
MIGRATE_VMA_SELECT_DEVICE_COHERENT,
.fault_page = page,
};
struct drm_gpusvm_zdd *zdd;
const struct drm_gpusvm_devmem_ops *ops;
struct device *dev = NULL;
unsigned long npages, mpages = 0;
struct page **pages;
dma_addr_t *dma_addr;
unsigned long start, end;
void *buf;
int i, err = 0;
if (page) {
zdd = page->zone_device_data;
if (time_before64(get_jiffies_64(),
zdd->devmem_allocation->timeslice_expiration))
return 0;
}
start = ALIGN_DOWN(fault_addr, size);
end = ALIGN(fault_addr + 1, size);
/* Corner where VMA area struct has been partially unmapped */
if (start < vas->vm_start)
start = vas->vm_start;
if (end > vas->vm_end)
end = vas->vm_end;
migrate.start = start;
migrate.end = end;
npages = npages_in_range(start, end);
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
sizeof(*pages), GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto err_out;
}
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
migrate.vma = vas;
migrate.src = buf;
migrate.dst = migrate.src + npages;
err = migrate_vma_setup(&migrate);
if (err)
goto err_free;
/* Raced with another CPU fault, nothing to do */
if (!migrate.cpages)
goto err_free;
if (!page) {
for (i = 0; i < npages; ++i) {
if (!(migrate.src[i] & MIGRATE_PFN_MIGRATE))
continue;
page = migrate_pfn_to_page(migrate.src[i]);
break;
}
if (!page)
goto err_finalize;
}
zdd = page->zone_device_data;
ops = zdd->devmem_allocation->ops;
dev = zdd->devmem_allocation->dev;
err = drm_gpusvm_migrate_populate_ram_pfn(vas, page, npages, &mpages,
migrate.src, migrate.dst,
start);
if (err)
goto err_finalize;
err = drm_gpusvm_migrate_map_pages(dev, dma_addr, migrate.dst, npages,
DMA_FROM_DEVICE);
if (err)
goto err_finalize;
for (i = 0; i < npages; ++i)
pages[i] = migrate_pfn_to_page(migrate.src[i]);
err = ops->copy_to_ram(pages, dma_addr, npages);
if (err)
goto err_finalize;
err_finalize:
if (err)
drm_gpusvm_migration_unlock_put_pages(npages, migrate.dst);
migrate_vma_pages(&migrate);
migrate_vma_finalize(&migrate);
if (dev)
drm_gpusvm_migrate_unmap_pages(dev, dma_addr, npages,
DMA_FROM_DEVICE);
err_free:
kvfree(buf);
err_out:
return err;
}
/**
* drm_gpusvm_range_evict - Evict GPU SVM range
* @range: Pointer to the GPU SVM range to be removed
*
* This function evicts the specified GPU SVM range. This function will not
* evict coherent pages.
* This function evicts the specified GPU SVM range.
*
* Return: 0 on success, a negative error code on failure.
*/
@@ -2183,60 +1524,6 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm,
}
EXPORT_SYMBOL_GPL(drm_gpusvm_range_evict);
/**
* drm_gpusvm_page_free() - Put GPU SVM zone device data associated with a page
* @page: Pointer to the page
*
* This function is a callback used to put the GPU SVM zone device data
* associated with a page when it is being released.
*/
static void drm_gpusvm_page_free(struct page *page)
{
drm_gpusvm_zdd_put(page->zone_device_data);
}
/**
* drm_gpusvm_migrate_to_ram() - Migrate GPU SVM range to RAM (page fault handler)
* @vmf: Pointer to the fault information structure
*
* This function is a page fault handler used to migrate a GPU SVM range to RAM.
* It retrieves the GPU SVM range information from the faulting page and invokes
* the internal migration function to migrate the range back to RAM.
*
* Return: VM_FAULT_SIGBUS on failure, 0 on success.
*/
static vm_fault_t drm_gpusvm_migrate_to_ram(struct vm_fault *vmf)
{
struct drm_gpusvm_zdd *zdd = vmf->page->zone_device_data;
int err;
err = __drm_gpusvm_migrate_to_ram(vmf->vma,
zdd->device_private_page_owner,
vmf->page, vmf->address,
zdd->devmem_allocation->size);
return err ? VM_FAULT_SIGBUS : 0;
}
/*
* drm_gpusvm_pagemap_ops - Device page map operations for GPU SVM
*/
static const struct dev_pagemap_ops drm_gpusvm_pagemap_ops = {
.page_free = drm_gpusvm_page_free,
.migrate_to_ram = drm_gpusvm_migrate_to_ram,
};
/**
* drm_gpusvm_pagemap_ops_get() - Retrieve GPU SVM device page map operations
*
* Return: Pointer to the GPU SVM device page map operations structure.
*/
const struct dev_pagemap_ops *drm_gpusvm_pagemap_ops_get(void)
{
return &drm_gpusvm_pagemap_ops;
}
EXPORT_SYMBOL_GPL(drm_gpusvm_pagemap_ops_get);
/**
* drm_gpusvm_has_mapping() - Check if GPU SVM has mapping for the given address range
* @gpusvm: Pointer to the GPU SVM structure.
@@ -2281,28 +1568,5 @@ void drm_gpusvm_range_set_unmapped(struct drm_gpusvm_range *range,
}
EXPORT_SYMBOL_GPL(drm_gpusvm_range_set_unmapped);
/**
* drm_gpusvm_devmem_init() - Initialize a GPU SVM device memory allocation
*
* @dev: Pointer to the device structure which device memory allocation belongs to
* @mm: Pointer to the mm_struct for the address space
* @ops: Pointer to the operations structure for GPU SVM device memory
* @dpagemap: The struct drm_pagemap we're allocating from.
* @size: Size of device memory allocation
*/
void drm_gpusvm_devmem_init(struct drm_gpusvm_devmem *devmem_allocation,
struct device *dev, struct mm_struct *mm,
const struct drm_gpusvm_devmem_ops *ops,
struct drm_pagemap *dpagemap, size_t size)
{
init_completion(&devmem_allocation->detached);
devmem_allocation->dev = dev;
devmem_allocation->mm = mm;
devmem_allocation->ops = ops;
devmem_allocation->dpagemap = dpagemap;
devmem_allocation->size = size;
}
EXPORT_SYMBOL_GPL(drm_gpusvm_devmem_init);
MODULE_DESCRIPTION("DRM GPUSVM");
MODULE_LICENSE("GPL");
+838
View File
@@ -0,0 +1,838 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*
* Copyright © 2024-2025 Intel Corporation
*/
#include <linux/dma-mapping.h>
#include <linux/migrate.h>
#include <linux/pagemap.h>
#include <drm/drm_drv.h>
#include <drm/drm_pagemap.h>
/**
* DOC: Overview
*
* The DRM pagemap layer is intended to augment the dev_pagemap functionality by
* providing a way to populate a struct mm_struct virtual range with device
* private pages and to provide helpers to abstract device memory allocations,
* to migrate memory back and forth between device memory and system RAM and
* to handle access (and in the future migration) between devices implementing
* a fast interconnect that is not necessarily visible to the rest of the
* system.
*
* Typically the DRM pagemap receives requests from one or more DRM GPU SVM
* instances to populate struct mm_struct virtual ranges with memory, and the
* migration is best effort only and may thus fail. The implementation should
* also handle device unbinding by blocking (return an -ENODEV) error for new
* population requests and after that migrate all device pages to system ram.
*/
/**
* DOC: Migration
*
* Migration granularity typically follows the GPU SVM range requests, but
* if there are clashes, due to races or due to the fact that multiple GPU
* SVM instances have different views of the ranges used, and because of that
* parts of a requested range is already present in the requested device memory,
* the implementation has a variety of options. It can fail and it can choose
* to populate only the part of the range that isn't already in device memory,
* and it can evict the range to system before trying to migrate. Ideally an
* implementation would just try to migrate the missing part of the range and
* allocate just enough memory to do so.
*
* When migrating to system memory as a response to a cpu fault or a device
* memory eviction request, currently a full device memory allocation is
* migrated back to system. Moving forward this might need improvement for
* situations where a single page needs bouncing between system memory and
* device memory due to, for example, atomic operations.
*
* Key DRM pagemap components:
*
* - Device Memory Allocations:
* Embedded structure containing enough information for the drm_pagemap to
* migrate to / from device memory.
*
* - Device Memory Operations:
* Define the interface for driver-specific device memory operations
* release memory, populate pfns, and copy to / from device memory.
*/
/**
* struct drm_pagemap_zdd - GPU SVM zone device data
*
* @refcount: Reference count for the zdd
* @devmem_allocation: device memory allocation
* @device_private_page_owner: Device private pages owner
*
* This structure serves as a generic wrapper installed in
* page->zone_device_data. It provides infrastructure for looking up a device
* memory allocation upon CPU page fault and asynchronously releasing device
* memory once the CPU has no page references. Asynchronous release is useful
* because CPU page references can be dropped in IRQ contexts, while releasing
* device memory likely requires sleeping locks.
*/
struct drm_pagemap_zdd {
struct kref refcount;
struct drm_pagemap_devmem *devmem_allocation;
void *device_private_page_owner;
};
/**
* drm_pagemap_zdd_alloc() - Allocate a zdd structure.
* @device_private_page_owner: Device private pages owner
*
* This function allocates and initializes a new zdd structure. It sets up the
* reference count and initializes the destroy work.
*
* Return: Pointer to the allocated zdd on success, ERR_PTR() on failure.
*/
static struct drm_pagemap_zdd *
drm_pagemap_zdd_alloc(void *device_private_page_owner)
{
struct drm_pagemap_zdd *zdd;
zdd = kmalloc(sizeof(*zdd), GFP_KERNEL);
if (!zdd)
return NULL;
kref_init(&zdd->refcount);
zdd->devmem_allocation = NULL;
zdd->device_private_page_owner = device_private_page_owner;
return zdd;
}
/**
* drm_pagemap_zdd_get() - Get a reference to a zdd structure.
* @zdd: Pointer to the zdd structure.
*
* This function increments the reference count of the provided zdd structure.
*
* Return: Pointer to the zdd structure.
*/
static struct drm_pagemap_zdd *drm_pagemap_zdd_get(struct drm_pagemap_zdd *zdd)
{
kref_get(&zdd->refcount);
return zdd;
}
/**
* drm_pagemap_zdd_destroy() - Destroy a zdd structure.
* @ref: Pointer to the reference count structure.
*
* This function queues the destroy_work of the zdd for asynchronous destruction.
*/
static void drm_pagemap_zdd_destroy(struct kref *ref)
{
struct drm_pagemap_zdd *zdd =
container_of(ref, struct drm_pagemap_zdd, refcount);
struct drm_pagemap_devmem *devmem = zdd->devmem_allocation;
if (devmem) {
complete_all(&devmem->detached);
if (devmem->ops->devmem_release)
devmem->ops->devmem_release(devmem);
}
kfree(zdd);
}
/**
* drm_pagemap_zdd_put() - Put a zdd reference.
* @zdd: Pointer to the zdd structure.
*
* This function decrements the reference count of the provided zdd structure
* and schedules its destruction if the count drops to zero.
*/
static void drm_pagemap_zdd_put(struct drm_pagemap_zdd *zdd)
{
kref_put(&zdd->refcount, drm_pagemap_zdd_destroy);
}
/**
* drm_pagemap_migration_unlock_put_page() - Put a migration page
* @page: Pointer to the page to put
*
* This function unlocks and puts a page.
*/
static void drm_pagemap_migration_unlock_put_page(struct page *page)
{
unlock_page(page);
put_page(page);
}
/**
* drm_pagemap_migration_unlock_put_pages() - Put migration pages
* @npages: Number of pages
* @migrate_pfn: Array of migrate page frame numbers
*
* This function unlocks and puts an array of pages.
*/
static void drm_pagemap_migration_unlock_put_pages(unsigned long npages,
unsigned long *migrate_pfn)
{
unsigned long i;
for (i = 0; i < npages; ++i) {
struct page *page;
if (!migrate_pfn[i])
continue;
page = migrate_pfn_to_page(migrate_pfn[i]);
drm_pagemap_migration_unlock_put_page(page);
migrate_pfn[i] = 0;
}
}
/**
* drm_pagemap_get_devmem_page() - Get a reference to a device memory page
* @page: Pointer to the page
* @zdd: Pointer to the GPU SVM zone device data
*
* This function associates the given page with the specified GPU SVM zone
* device data and initializes it for zone device usage.
*/
static void drm_pagemap_get_devmem_page(struct page *page,
struct drm_pagemap_zdd *zdd)
{
page->zone_device_data = drm_pagemap_zdd_get(zdd);
zone_device_page_init(page);
}
/**
* drm_pagemap_migrate_map_pages() - Map migration pages for GPU SVM migration
* @dev: The device for which the pages are being mapped
* @dma_addr: Array to store DMA addresses corresponding to mapped pages
* @migrate_pfn: Array of migrate page frame numbers to map
* @npages: Number of pages to map
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
*
* This function maps pages of memory for migration usage in GPU SVM. It
* iterates over each page frame number provided in @migrate_pfn, maps the
* corresponding page, and stores the DMA address in the provided @dma_addr
* array.
*
* Returns: 0 on success, -EFAULT if an error occurs during mapping.
*/
static int drm_pagemap_migrate_map_pages(struct device *dev,
dma_addr_t *dma_addr,
unsigned long *migrate_pfn,
unsigned long npages,
enum dma_data_direction dir)
{
unsigned long i;
for (i = 0; i < npages; ++i) {
struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
if (!page)
continue;
if (WARN_ON_ONCE(is_zone_device_page(page)))
return -EFAULT;
dma_addr[i] = dma_map_page(dev, page, 0, PAGE_SIZE, dir);
if (dma_mapping_error(dev, dma_addr[i]))
return -EFAULT;
}
return 0;
}
/**
* drm_pagemap_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration
* @dev: The device for which the pages were mapped
* @dma_addr: Array of DMA addresses corresponding to mapped pages
* @npages: Number of pages to unmap
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
*
* This function unmaps previously mapped pages of memory for GPU Shared Virtual
* Memory (SVM). It iterates over each DMA address provided in @dma_addr, checks
* if it's valid and not already unmapped, and unmaps the corresponding page.
*/
static void drm_pagemap_migrate_unmap_pages(struct device *dev,
dma_addr_t *dma_addr,
unsigned long npages,
enum dma_data_direction dir)
{
unsigned long i;
for (i = 0; i < npages; ++i) {
if (!dma_addr[i] || dma_mapping_error(dev, dma_addr[i]))
continue;
dma_unmap_page(dev, dma_addr[i], PAGE_SIZE, dir);
}
}
static unsigned long
npages_in_range(unsigned long start, unsigned long end)
{
return (end - start) >> PAGE_SHIFT;
}
/**
* drm_pagemap_migrate_to_devmem() - Migrate a struct mm_struct range to device memory
* @devmem_allocation: The device memory allocation to migrate to.
* The caller should hold a reference to the device memory allocation,
* and the reference is consumed by this function unless it returns with
* an error.
* @mm: Pointer to the struct mm_struct.
* @start: Start of the virtual address range to migrate.
* @end: End of the virtual address range to migrate.
* @timeslice_ms: The time requested for the migrated pagemap pages to
* be present in @mm before being allowed to be migrated back.
* @pgmap_owner: Not used currently, since only system memory is considered.
*
* This function migrates the specified virtual address range to device memory.
* It performs the necessary setup and invokes the driver-specific operations for
* migration to device memory. Expected to be called while holding the mmap lock in
* at least read mode.
*
* Note: The @timeslice_ms parameter can typically be used to force data to
* remain in pagemap pages long enough for a GPU to perform a task and to prevent
* a migration livelock. One alternative would be for the GPU driver to block
* in a mmu_notifier for the specified amount of time, but adding the
* functionality to the pagemap is likely nicer to the system as a whole.
*
* Return: %0 on success, negative error code on failure.
*/
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
struct mm_struct *mm,
unsigned long start, unsigned long end,
unsigned long timeslice_ms,
void *pgmap_owner)
{
const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;
struct migrate_vma migrate = {
.start = start,
.end = end,
.pgmap_owner = pgmap_owner,
.flags = MIGRATE_VMA_SELECT_SYSTEM,
};
unsigned long i, npages = npages_in_range(start, end);
struct vm_area_struct *vas;
struct drm_pagemap_zdd *zdd = NULL;
struct page **pages;
dma_addr_t *dma_addr;
void *buf;
int err;
mmap_assert_locked(mm);
if (!ops->populate_devmem_pfn || !ops->copy_to_devmem ||
!ops->copy_to_ram)
return -EOPNOTSUPP;
vas = vma_lookup(mm, start);
if (!vas) {
err = -ENOENT;
goto err_out;
}
if (end > vas->vm_end || start < vas->vm_start) {
err = -EINVAL;
goto err_out;
}
if (!vma_is_anonymous(vas)) {
err = -EBUSY;
goto err_out;
}
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
sizeof(*pages), GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto err_out;
}
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
zdd = drm_pagemap_zdd_alloc(pgmap_owner);
if (!zdd) {
err = -ENOMEM;
goto err_free;
}
migrate.vma = vas;
migrate.src = buf;
migrate.dst = migrate.src + npages;
err = migrate_vma_setup(&migrate);
if (err)
goto err_free;
if (!migrate.cpages) {
err = -EFAULT;
goto err_free;
}
if (migrate.cpages != npages) {
err = -EBUSY;
goto err_finalize;
}
err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst);
if (err)
goto err_finalize;
err = drm_pagemap_migrate_map_pages(devmem_allocation->dev, dma_addr,
migrate.src, npages, DMA_TO_DEVICE);
if (err)
goto err_finalize;
for (i = 0; i < npages; ++i) {
struct page *page = pfn_to_page(migrate.dst[i]);
pages[i] = page;
migrate.dst[i] = migrate_pfn(migrate.dst[i]);
drm_pagemap_get_devmem_page(page, zdd);
}
err = ops->copy_to_devmem(pages, dma_addr, npages);
if (err)
goto err_finalize;
/* Upon success bind devmem allocation to range and zdd */
devmem_allocation->timeslice_expiration = get_jiffies_64() +
msecs_to_jiffies(timeslice_ms);
zdd->devmem_allocation = devmem_allocation; /* Owns ref */
err_finalize:
if (err)
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
migrate_vma_pages(&migrate);
migrate_vma_finalize(&migrate);
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
DMA_TO_DEVICE);
err_free:
if (zdd)
drm_pagemap_zdd_put(zdd);
kvfree(buf);
err_out:
return err;
}
EXPORT_SYMBOL_GPL(drm_pagemap_migrate_to_devmem);
/**
* drm_pagemap_migrate_populate_ram_pfn() - Populate RAM PFNs for a VM area
* @vas: Pointer to the VM area structure, can be NULL
* @fault_page: Fault page
* @npages: Number of pages to populate
* @mpages: Number of pages to migrate
* @src_mpfn: Source array of migrate PFNs
* @mpfn: Array of migrate PFNs to populate
* @addr: Start address for PFN allocation
*
* This function populates the RAM migrate page frame numbers (PFNs) for the
* specified VM area structure. It allocates and locks pages in the VM area for
* RAM usage. If vas is non-NULL use alloc_page_vma for allocation, if NULL use
* alloc_page for allocation.
*
* Return: 0 on success, negative error code on failure.
*/
static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
struct page *fault_page,
unsigned long npages,
unsigned long *mpages,
unsigned long *src_mpfn,
unsigned long *mpfn,
unsigned long addr)
{
unsigned long i;
for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
struct page *page, *src_page;
if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE))
continue;
src_page = migrate_pfn_to_page(src_mpfn[i]);
if (!src_page)
continue;
if (fault_page) {
if (src_page->zone_device_data !=
fault_page->zone_device_data)
continue;
}
if (vas)
page = alloc_page_vma(GFP_HIGHUSER, vas, addr);
else
page = alloc_page(GFP_HIGHUSER);
if (!page)
goto free_pages;
mpfn[i] = migrate_pfn(page_to_pfn(page));
}
for (i = 0; i < npages; ++i) {
struct page *page = migrate_pfn_to_page(mpfn[i]);
if (!page)
continue;
WARN_ON_ONCE(!trylock_page(page));
++*mpages;
}
return 0;
free_pages:
for (i = 0; i < npages; ++i) {
struct page *page = migrate_pfn_to_page(mpfn[i]);
if (!page)
continue;
put_page(page);
mpfn[i] = 0;
}
return -ENOMEM;
}
/**
* drm_pagemap_evict_to_ram() - Evict GPU SVM range to RAM
* @devmem_allocation: Pointer to the device memory allocation
*
* Similar to __drm_pagemap_migrate_to_ram but does not require mmap lock and
* migration done via migrate_device_* functions.
*
* Return: 0 on success, negative error code on failure.
*/
int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)
{
const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;
unsigned long npages, mpages = 0;
struct page **pages;
unsigned long *src, *dst;
dma_addr_t *dma_addr;
void *buf;
int i, err = 0;
unsigned int retry_count = 2;
npages = devmem_allocation->size >> PAGE_SHIFT;
retry:
if (!mmget_not_zero(devmem_allocation->mm))
return -EFAULT;
buf = kvcalloc(npages, 2 * sizeof(*src) + sizeof(*dma_addr) +
sizeof(*pages), GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto err_out;
}
src = buf;
dst = buf + (sizeof(*src) * npages);
dma_addr = buf + (2 * sizeof(*src) * npages);
pages = buf + (2 * sizeof(*src) + sizeof(*dma_addr)) * npages;
err = ops->populate_devmem_pfn(devmem_allocation, npages, src);
if (err)
goto err_free;
err = migrate_device_pfns(src, npages);
if (err)
goto err_free;
err = drm_pagemap_migrate_populate_ram_pfn(NULL, NULL, npages, &mpages,
src, dst, 0);
if (err || !mpages)
goto err_finalize;
err = drm_pagemap_migrate_map_pages(devmem_allocation->dev, dma_addr,
dst, npages, DMA_FROM_DEVICE);
if (err)
goto err_finalize;
for (i = 0; i < npages; ++i)
pages[i] = migrate_pfn_to_page(src[i]);
err = ops->copy_to_ram(pages, dma_addr, npages);
if (err)
goto err_finalize;
err_finalize:
if (err)
drm_pagemap_migration_unlock_put_pages(npages, dst);
migrate_device_pages(src, dst, npages);
migrate_device_finalize(src, dst, npages);
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
DMA_FROM_DEVICE);
err_free:
kvfree(buf);
err_out:
mmput_async(devmem_allocation->mm);
if (completion_done(&devmem_allocation->detached))
return 0;
if (retry_count--) {
cond_resched();
goto retry;
}
return err ?: -EBUSY;
}
EXPORT_SYMBOL_GPL(drm_pagemap_evict_to_ram);
/**
* __drm_pagemap_migrate_to_ram() - Migrate GPU SVM range to RAM (internal)
* @vas: Pointer to the VM area structure
* @device_private_page_owner: Device private pages owner
* @page: Pointer to the page for fault handling (can be NULL)
* @fault_addr: Fault address
* @size: Size of migration
*
* This internal function performs the migration of the specified GPU SVM range
* to RAM. It sets up the migration, populates + dma maps RAM PFNs, and
* invokes the driver-specific operations for migration to RAM.
*
* Return: 0 on success, negative error code on failure.
*/
static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
void *device_private_page_owner,
struct page *page,
unsigned long fault_addr,
unsigned long size)
{
struct migrate_vma migrate = {
.vma = vas,
.pgmap_owner = device_private_page_owner,
.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE |
MIGRATE_VMA_SELECT_DEVICE_COHERENT,
.fault_page = page,
};
struct drm_pagemap_zdd *zdd;
const struct drm_pagemap_devmem_ops *ops;
struct device *dev = NULL;
unsigned long npages, mpages = 0;
struct page **pages;
dma_addr_t *dma_addr;
unsigned long start, end;
void *buf;
int i, err = 0;
if (page) {
zdd = page->zone_device_data;
if (time_before64(get_jiffies_64(),
zdd->devmem_allocation->timeslice_expiration))
return 0;
}
start = ALIGN_DOWN(fault_addr, size);
end = ALIGN(fault_addr + 1, size);
/* Corner where VMA area struct has been partially unmapped */
if (start < vas->vm_start)
start = vas->vm_start;
if (end > vas->vm_end)
end = vas->vm_end;
migrate.start = start;
migrate.end = end;
npages = npages_in_range(start, end);
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
sizeof(*pages), GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto err_out;
}
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
migrate.vma = vas;
migrate.src = buf;
migrate.dst = migrate.src + npages;
err = migrate_vma_setup(&migrate);
if (err)
goto err_free;
/* Raced with another CPU fault, nothing to do */
if (!migrate.cpages)
goto err_free;
if (!page) {
for (i = 0; i < npages; ++i) {
if (!(migrate.src[i] & MIGRATE_PFN_MIGRATE))
continue;
page = migrate_pfn_to_page(migrate.src[i]);
break;
}
if (!page)
goto err_finalize;
}
zdd = page->zone_device_data;
ops = zdd->devmem_allocation->ops;
dev = zdd->devmem_allocation->dev;
err = drm_pagemap_migrate_populate_ram_pfn(vas, page, npages, &mpages,
migrate.src, migrate.dst,
start);
if (err)
goto err_finalize;
err = drm_pagemap_migrate_map_pages(dev, dma_addr, migrate.dst, npages,
DMA_FROM_DEVICE);
if (err)
goto err_finalize;
for (i = 0; i < npages; ++i)
pages[i] = migrate_pfn_to_page(migrate.src[i]);
err = ops->copy_to_ram(pages, dma_addr, npages);
if (err)
goto err_finalize;
err_finalize:
if (err)
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
migrate_vma_pages(&migrate);
migrate_vma_finalize(&migrate);
if (dev)
drm_pagemap_migrate_unmap_pages(dev, dma_addr, npages,
DMA_FROM_DEVICE);
err_free:
kvfree(buf);
err_out:
return err;
}
/**
* drm_pagemap_page_free() - Put GPU SVM zone device data associated with a page
* @page: Pointer to the page
*
* This function is a callback used to put the GPU SVM zone device data
* associated with a page when it is being released.
*/
static void drm_pagemap_page_free(struct page *page)
{
drm_pagemap_zdd_put(page->zone_device_data);
}
/**
* drm_pagemap_migrate_to_ram() - Migrate a virtual range to RAM (page fault handler)
* @vmf: Pointer to the fault information structure
*
* This function is a page fault handler used to migrate a virtual range
* to ram. The device memory allocation in which the device page is found is
* migrated in its entirety.
*
* Returns:
* VM_FAULT_SIGBUS on failure, 0 on success.
*/
static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf)
{
struct drm_pagemap_zdd *zdd = vmf->page->zone_device_data;
int err;
err = __drm_pagemap_migrate_to_ram(vmf->vma,
zdd->device_private_page_owner,
vmf->page, vmf->address,
zdd->devmem_allocation->size);
return err ? VM_FAULT_SIGBUS : 0;
}
static const struct dev_pagemap_ops drm_pagemap_pagemap_ops = {
.page_free = drm_pagemap_page_free,
.migrate_to_ram = drm_pagemap_migrate_to_ram,
};
/**
* drm_pagemap_pagemap_ops_get() - Retrieve GPU SVM device page map operations
*
* Returns:
* Pointer to the GPU SVM device page map operations structure.
*/
const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void)
{
return &drm_pagemap_pagemap_ops;
}
EXPORT_SYMBOL_GPL(drm_pagemap_pagemap_ops_get);
/**
* drm_pagemap_devmem_init() - Initialize a drm_pagemap device memory allocation
*
* @devmem_allocation: The struct drm_pagemap_devmem to initialize.
* @dev: Pointer to the device structure which device memory allocation belongs to
* @mm: Pointer to the mm_struct for the address space
* @ops: Pointer to the operations structure for GPU SVM device memory
* @dpagemap: The struct drm_pagemap we're allocating from.
* @size: Size of device memory allocation
*/
void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation,
struct device *dev, struct mm_struct *mm,
const struct drm_pagemap_devmem_ops *ops,
struct drm_pagemap *dpagemap, size_t size)
{
init_completion(&devmem_allocation->detached);
devmem_allocation->dev = dev;
devmem_allocation->mm = mm;
devmem_allocation->ops = ops;
devmem_allocation->dpagemap = dpagemap;
devmem_allocation->size = size;
}
EXPORT_SYMBOL_GPL(drm_pagemap_devmem_init);
/**
* drm_pagemap_page_to_dpagemap() - Return a pointer the drm_pagemap of a page
* @page: The struct page.
*
* Return: A pointer to the struct drm_pagemap of a device private page that
* was populated from the struct drm_pagemap. If the page was *not* populated
* from a struct drm_pagemap, the result is undefined and the function call
* may result in dereferencing and invalid address.
*/
struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page)
{
struct drm_pagemap_zdd *zdd = page->zone_device_data;
return zdd->devmem_allocation->dpagemap;
}
EXPORT_SYMBOL_GPL(drm_pagemap_page_to_dpagemap);
/**
* drm_pagemap_populate_mm() - Populate a virtual range with device memory pages
* @dpagemap: Pointer to the drm_pagemap managing the device memory
* @start: Start of the virtual range to populate.
* @end: End of the virtual range to populate.
* @mm: Pointer to the virtual address space.
* @timeslice_ms: The time requested for the migrated pagemap pages to
* be present in @mm before being allowed to be migrated back.
*
* Attempt to populate a virtual range with device memory pages,
* clearing them or migrating data from the existing pages if necessary.
* The function is best effort only, and implementations may vary
* in how hard they try to satisfy the request.
*
* Return: %0 on success, negative error code on error. If the hardware
* device was removed / unbound the function will return %-ENODEV.
*/
int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
unsigned long start, unsigned long end,
struct mm_struct *mm,
unsigned long timeslice_ms)
{
int err;
if (!mmget_not_zero(mm))
return -EFAULT;
mmap_read_lock(mm);
err = dpagemap->ops->populate_mm(dpagemap, start, end, mm,
timeslice_ms);
mmap_read_unlock(mm);
mmput(mm);
return err;
}
EXPORT_SYMBOL(drm_pagemap_populate_mm);
+10 -6
View File
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_XE
tristate "Intel Xe2 Graphics"
depends on DRM && PCI && (m || (y && KUNIT=y))
depends on DRM && PCI
depends on KUNIT || !KUNIT
depends on INTEL_VSEC || !INTEL_VSEC
depends on X86_PLATFORM_DEVICES || !(X86 && ACPI)
select INTERVAL_TREE
@@ -44,6 +45,7 @@ config DRM_XE
select WANT_DEV_COREDUMP
select AUXILIARY_BUS
select HMM_MIRROR
select REGMAP if I2C
help
Driver for Intel Xe2 series GPUs and later. Experimental support
for Xe series is also available.
@@ -85,16 +87,18 @@ config DRM_XE_GPUSVM
Enable this option if you want support for CPU to GPU address
mirroring.
If in doubut say "Y".
If in doubt say "Y".
config DRM_XE_DEVMEM_MIRROR
bool "Enable device memory mirror"
config DRM_XE_PAGEMAP
bool "Enable device memory pool for SVM"
depends on DRM_XE_GPUSVM
select GET_FREE_REGION
default y
help
Disable this option only if you want to compile out without device
memory mirror. Will reduce KMD memory footprint when disabled.
Disable this option only if you don't want to expose local device
memory for SVM. Will reduce KMD memory footprint when disabled.
If in doubt say "Y".
config DRM_XE_FORCE_PROBE
string "Force probe xe for selected Intel hardware IDs"
+2
View File
@@ -80,6 +80,7 @@ xe-y += xe_bb.o \
xe_mmio.o \
xe_mocs.o \
xe_module.o \
xe_nvm.o \
xe_oa.o \
xe_observation.o \
xe_pat.o \
@@ -124,6 +125,7 @@ xe-y += xe_bb.o \
xe_wait_user_fence.o \
xe_wopcm.o
xe-$(CONFIG_I2C) += xe_i2c.o
xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
+4
View File
@@ -142,6 +142,7 @@ enum xe_guc_action {
XE_GUC_ACTION_SET_ENG_UTIL_BUFF = 0x550A,
XE_GUC_ACTION_SET_DEVICE_ENGINE_ACTIVITY_BUFFER = 0x550C,
XE_GUC_ACTION_SET_FUNCTION_ENGINE_ACTIVITY_BUFFER = 0x550D,
XE_GUC_ACTION_OPT_IN_FEATURE_KLV = 0x550E,
XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR = 0x6000,
XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC = 0x6002,
XE_GUC_ACTION_PAGE_FAULT_RES_DESC = 0x6003,
@@ -271,4 +272,7 @@ enum xe_guc_g2g_type {
#define XE_G2G_DEREGISTER_TILE REG_GENMASK(15, 12)
#define XE_G2G_DEREGISTER_TYPE REG_GENMASK(11, 8)
/* invalid type for XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR */
#define XE_GUC_CAT_ERR_TYPE_INVALID 0xdeadbeef
#endif
+28
View File
@@ -16,6 +16,7 @@
* +===+=======+==============================================================+
* | 0 | 31:16 | **KEY** - KLV key identifier |
* | | | - `GuC Self Config KLVs`_ |
* | | | - `GuC Opt In Feature KLVs`_ |
* | | | - `GuC VGT Policy KLVs`_ |
* | | | - `GuC VF Configuration KLVs`_ |
* | | | |
@@ -124,6 +125,33 @@ enum {
GUC_CONTEXT_POLICIES_KLV_NUM_IDS = 5,
};
/**
* DOC: GuC Opt In Feature KLVs
*
* `GuC KLV`_ keys available for use with OPT_IN_FEATURE_KLV
*
* _`GUC_KLV_OPT_IN_FEATURE_EXT_CAT_ERR_TYPE` : 0x4001
* Adds an extra dword to the XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR G2H
* containing the type of the CAT error. On HW that does not support
* reporting the CAT error type, the extra dword is set to 0xdeadbeef.
*
* _`GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH` : 0x4003
* This KLV enables the Dynamic Inhibit Context Switch optimization, which
* consists in the GuC setting the CTX_CTRL_INHIBIT_SYN_CTX_SWITCH bit to
* zero in the CTX_CONTEXT_CONTROL register of LRCs that are submitted
* to an oversubscribed engine. This will cause those contexts to be
* switched out immediately if they hit an unsatisfied semaphore wait
* (instead of waiting the full timeslice duration). The bit is instead set
* to one if a single context is queued on the engine, to avoid it being
* switched out if there isn't another context that can run in its place.
*/
#define GUC_KLV_OPT_IN_FEATURE_EXT_CAT_ERR_TYPE_KEY 0x4001
#define GUC_KLV_OPT_IN_FEATURE_EXT_CAT_ERR_TYPE_LEN 0u
#define GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH_KEY 0x4003
#define GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH_LEN 0u
/**
* DOC: GuC VGT Policy KLVs
*
+4 -7
View File
@@ -17,10 +17,7 @@ u32 intel_dsb_buffer_ggtt_offset(struct intel_dsb_buffer *dsb_buf)
void intel_dsb_buffer_write(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val)
{
struct xe_device *xe = dsb_buf->vma->bo->tile->xe;
iosys_map_wr(&dsb_buf->vma->bo->vmap, idx * 4, u32, val);
xe_device_l2_flush(xe);
}
u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx)
@@ -30,12 +27,9 @@ u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx)
void intel_dsb_buffer_memset(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val, size_t size)
{
struct xe_device *xe = dsb_buf->vma->bo->tile->xe;
WARN_ON(idx > (dsb_buf->buf_size - size) / sizeof(*dsb_buf->cmd_buf));
iosys_map_memset(&dsb_buf->vma->bo->vmap, idx * 4, val, size);
xe_device_l2_flush(xe);
}
bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *dsb_buf, size_t size)
@@ -74,9 +68,12 @@ void intel_dsb_buffer_cleanup(struct intel_dsb_buffer *dsb_buf)
void intel_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf)
{
struct xe_device *xe = dsb_buf->vma->bo->tile->xe;
/*
* The memory barrier here is to ensure coherency of DSB vs MMIO,
* both for weak ordering archs and discrete cards.
*/
xe_device_wmb(dsb_buf->vma->bo->tile->xe);
xe_device_wmb(xe);
xe_device_l2_flush(xe);
}
+4 -3
View File
@@ -163,6 +163,9 @@ static int __xe_pin_fb_vma_dpt(const struct intel_framebuffer *fb,
vma->dpt = dpt;
vma->node = dpt->ggtt_node[tile0->id];
/* Ensure DPT writes are flushed */
xe_device_l2_flush(xe);
return 0;
}
@@ -224,7 +227,7 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
goto out_unlock;
}
ret = xe_ggtt_node_insert_locked(vma->node, bo->size, align, 0);
ret = xe_ggtt_node_insert_locked(vma->node, xe_bo_size(bo), align, 0);
if (ret) {
xe_ggtt_node_fini(vma->node);
goto out_unlock;
@@ -326,8 +329,6 @@ static struct i915_vma *__xe_pin_fb_vma(const struct intel_framebuffer *fb,
if (ret)
goto err_unpin;
/* Ensure DPT writes are flushed */
xe_device_l2_flush(xe);
return vma;
err_unpin:
+1 -1
View File
@@ -85,7 +85,7 @@ static int intel_hdcp_gsc_initialize_message(struct xe_device *xe,
cmd_in = xe_bo_ggtt_addr(bo);
cmd_out = cmd_in + PAGE_SIZE;
xe_map_memset(xe, &bo->vmap, 0, 0, bo->size);
xe_map_memset(xe, &bo->vmap, 0, 0, xe_bo_size(bo));
gsc_context->hdcp_bo = bo;
gsc_context->hdcp_cmd_in = cmd_in;
+4
View File
@@ -16,6 +16,10 @@
#define MTL_GSC_HECI1_BASE 0x00116000
#define MTL_GSC_HECI2_BASE 0x00117000
#define DG1_GSC_HECI2_BASE 0x00259000
#define PVC_GSC_HECI2_BASE 0x00285000
#define DG2_GSC_HECI2_BASE 0x00374000
#define HECI_H_CSR(base) XE_REG((base) + 0x4)
#define HECI_H_CSR_IE REG_BIT(0)
#define HECI_H_CSR_IS REG_BIT(1)
+20
View File
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: MIT */
#ifndef _XE_I2C_REGS_H_
#define _XE_I2C_REGS_H_
#include <linux/pci_regs.h>
#include "xe_reg_defs.h"
#include "xe_regs.h"
#define I2C_BRIDGE_OFFSET (SOC_BASE + 0xd9000)
#define I2C_CONFIG_SPACE_OFFSET (SOC_BASE + 0xf6000)
#define I2C_MEM_SPACE_OFFSET (SOC_BASE + 0xf7400)
#define REG_SG_REMAP_ADDR_PREFIX XE_REG(SOC_BASE + 0x0164)
#define REG_SG_REMAP_ADDR_POSTFIX XE_REG(SOC_BASE + 0x0168)
#define I2C_CONFIG_CMD XE_REG(I2C_CONFIG_SPACE_OFFSET + PCI_COMMAND)
#define I2C_CONFIG_PMCSR XE_REG(I2C_CONFIG_SPACE_OFFSET + 0x84)
#endif /* _XE_I2C_REGS_H_ */
+1
View File
@@ -19,6 +19,7 @@
#define MASTER_IRQ REG_BIT(31)
#define GU_MISC_IRQ REG_BIT(29)
#define DISPLAY_IRQ REG_BIT(16)
#define I2C_IRQ REG_BIT(12)
#define GT_DW_IRQ(x) REG_BIT(x)
/*
+1 -1
View File
@@ -5,7 +5,7 @@
#ifndef _XE_PMT_H_
#define _XE_PMT_H_
#define SOC_BASE 0x280000
#include "xe_regs.h"
#define BMG_PMT_BASE_OFFSET 0xDB000
#define BMG_DISCOVERY_OFFSET (SOC_BASE + BMG_PMT_BASE_OFFSET)
+2
View File
@@ -7,6 +7,8 @@
#include "regs/xe_reg_defs.h"
#define SOC_BASE 0x280000
#define GU_CNTL_PROTECTED XE_REG(0x10100C)
#define DRIVERINT_FLR_DIS REG_BIT(31)
+1 -1
View File
@@ -106,7 +106,7 @@ static int ccs_test_migrate(struct xe_tile *tile, struct xe_bo *bo,
}
/* Check last CCS value, or at least last value in page. */
offset = xe_device_ccs_bytes(tile_to_xe(tile), bo->size);
offset = xe_device_ccs_bytes(tile_to_xe(tile), xe_bo_size(bo));
offset = min_t(u32, offset, PAGE_SIZE) / sizeof(u64) - 1;
if (cpu_map[offset] != get_val) {
KUNIT_FAIL(test,
+2 -2
View File
@@ -32,7 +32,7 @@ static struct xe_bo *replacement_xe_managed_bo_create_pin_map(struct xe_device *
bo->tile = tile;
bo->ttm.bdev = &xe->ttm;
bo->size = size;
bo->ttm.base.size = size;
iosys_map_set_vaddr(&bo->vmap, buf);
if (flags & XE_BO_FLAG_GGTT) {
@@ -43,7 +43,7 @@ static struct xe_bo *replacement_xe_managed_bo_create_pin_map(struct xe_device *
KUNIT_ASSERT_EQ(test, 0,
xe_ggtt_node_insert(bo->ggtt_node[tile->id],
bo->size, SZ_4K));
xe_bo_size(bo), SZ_4K));
}
return bo;
+26 -26
View File
@@ -74,13 +74,13 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
{
struct xe_device *xe = tile_to_xe(m->tile);
u64 retval, expected = 0;
bool big = bo->size >= SZ_2M;
bool big = xe_bo_size(bo) >= SZ_2M;
struct dma_fence *fence;
const char *str = big ? "Copying big bo" : "Copying small bo";
int err;
struct xe_bo *remote = xe_bo_create_locked(xe, m->tile, NULL,
bo->size,
xe_bo_size(bo),
ttm_bo_type_kernel,
region |
XE_BO_FLAG_NEEDS_CPU_ACCESS |
@@ -105,7 +105,7 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
goto out_unlock;
}
xe_map_memset(xe, &remote->vmap, 0, 0xd0, remote->size);
xe_map_memset(xe, &remote->vmap, 0, 0xd0, xe_bo_size(remote));
fence = xe_migrate_clear(m, remote, remote->ttm.resource,
XE_MIGRATE_CLEAR_FLAG_FULL);
if (!sanity_fence_failed(xe, fence, big ? "Clearing remote big bo" :
@@ -113,15 +113,15 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
retval = xe_map_rd(xe, &remote->vmap, 0, u64);
check(retval, expected, "remote first offset should be cleared",
test);
retval = xe_map_rd(xe, &remote->vmap, remote->size - 8, u64);
retval = xe_map_rd(xe, &remote->vmap, xe_bo_size(remote) - 8, u64);
check(retval, expected, "remote last offset should be cleared",
test);
}
dma_fence_put(fence);
/* Try to copy 0xc0 from remote to vram with 2MB or 64KiB/4KiB pages */
xe_map_memset(xe, &remote->vmap, 0, 0xc0, remote->size);
xe_map_memset(xe, &bo->vmap, 0, 0xd0, bo->size);
xe_map_memset(xe, &remote->vmap, 0, 0xc0, xe_bo_size(remote));
xe_map_memset(xe, &bo->vmap, 0, 0xd0, xe_bo_size(bo));
expected = 0xc0c0c0c0c0c0c0c0;
fence = xe_migrate_copy(m, remote, bo, remote->ttm.resource,
@@ -131,15 +131,15 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
retval = xe_map_rd(xe, &bo->vmap, 0, u64);
check(retval, expected,
"remote -> vram bo first offset should be copied", test);
retval = xe_map_rd(xe, &bo->vmap, bo->size - 8, u64);
retval = xe_map_rd(xe, &bo->vmap, xe_bo_size(bo) - 8, u64);
check(retval, expected,
"remote -> vram bo offset should be copied", test);
}
dma_fence_put(fence);
/* And other way around.. slightly hacky.. */
xe_map_memset(xe, &remote->vmap, 0, 0xd0, remote->size);
xe_map_memset(xe, &bo->vmap, 0, 0xc0, bo->size);
xe_map_memset(xe, &remote->vmap, 0, 0xd0, xe_bo_size(remote));
xe_map_memset(xe, &bo->vmap, 0, 0xc0, xe_bo_size(bo));
fence = xe_migrate_copy(m, bo, remote, bo->ttm.resource,
remote->ttm.resource, false);
@@ -148,7 +148,7 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
retval = xe_map_rd(xe, &remote->vmap, 0, u64);
check(retval, expected,
"vram -> remote bo first offset should be copied", test);
retval = xe_map_rd(xe, &remote->vmap, bo->size - 8, u64);
retval = xe_map_rd(xe, &remote->vmap, xe_bo_size(bo) - 8, u64);
check(retval, expected,
"vram -> remote bo last offset should be copied", test);
}
@@ -245,9 +245,9 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
if (m->q->vm->flags & XE_VM_FLAG_64K)
expected |= XE_PTE_PS64;
if (xe_bo_is_vram(pt))
xe_res_first(pt->ttm.resource, 0, pt->size, &src_it);
xe_res_first(pt->ttm.resource, 0, xe_bo_size(pt), &src_it);
else
xe_res_first_sg(xe_bo_sg(pt), 0, pt->size, &src_it);
xe_res_first_sg(xe_bo_sg(pt), 0, xe_bo_size(pt), &src_it);
emit_pte(m, bb, NUM_KERNEL_PDE - 1, xe_bo_is_vram(pt), false,
&src_it, XE_PAGE_SIZE, pt->ttm.resource);
@@ -276,7 +276,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
/* Clear a small bo */
kunit_info(test, "Clearing small buffer object\n");
xe_map_memset(xe, &tiny->vmap, 0, 0x22, tiny->size);
xe_map_memset(xe, &tiny->vmap, 0, 0x22, xe_bo_size(tiny));
expected = 0;
fence = xe_migrate_clear(m, tiny, tiny->ttm.resource,
XE_MIGRATE_CLEAR_FLAG_FULL);
@@ -286,7 +286,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
dma_fence_put(fence);
retval = xe_map_rd(xe, &tiny->vmap, 0, u32);
check(retval, expected, "Command clear small first value", test);
retval = xe_map_rd(xe, &tiny->vmap, tiny->size - 4, u32);
retval = xe_map_rd(xe, &tiny->vmap, xe_bo_size(tiny) - 4, u32);
check(retval, expected, "Command clear small last value", test);
kunit_info(test, "Copying small buffer object to system\n");
@@ -298,7 +298,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
/* Clear a big bo */
kunit_info(test, "Clearing big buffer object\n");
xe_map_memset(xe, &big->vmap, 0, 0x11, big->size);
xe_map_memset(xe, &big->vmap, 0, 0x11, xe_bo_size(big));
expected = 0;
fence = xe_migrate_clear(m, big, big->ttm.resource,
XE_MIGRATE_CLEAR_FLAG_FULL);
@@ -308,7 +308,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
dma_fence_put(fence);
retval = xe_map_rd(xe, &big->vmap, 0, u32);
check(retval, expected, "Command clear big first value", test);
retval = xe_map_rd(xe, &big->vmap, big->size - 4, u32);
retval = xe_map_rd(xe, &big->vmap, xe_bo_size(big) - 4, u32);
check(retval, expected, "Command clear big last value", test);
kunit_info(test, "Copying big buffer object to system\n");
@@ -370,7 +370,7 @@ static struct dma_fence *blt_copy(struct xe_tile *tile,
struct xe_migrate *m = tile->migrate;
struct xe_device *xe = gt_to_xe(gt);
struct dma_fence *fence = NULL;
u64 size = src_bo->size;
u64 size = xe_bo_size(src_bo);
struct xe_res_cursor src_it, dst_it;
struct ttm_resource *src = src_bo->ttm.resource, *dst = dst_bo->ttm.resource;
u64 src_L0_ofs, dst_L0_ofs;
@@ -498,7 +498,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
long ret;
expected = 0xd0d0d0d0d0d0d0d0;
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, sys_bo->size);
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, xe_bo_size(sys_bo));
fence = blt_copy(tile, sys_bo, vram_bo, false, "Blit copy from sysmem to vram", test);
if (!sanity_fence_failed(xe, fence, "Blit copy from sysmem to vram", test)) {
@@ -523,7 +523,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
retval = xe_map_rd(xe, &vram_bo->vmap, 0, u64);
check(retval, expected, "Clear evicted vram data first value", test);
retval = xe_map_rd(xe, &vram_bo->vmap, vram_bo->size - 8, u64);
retval = xe_map_rd(xe, &vram_bo->vmap, xe_bo_size(vram_bo) - 8, u64);
check(retval, expected, "Clear evicted vram data last value", test);
fence = blt_copy(tile, vram_bo, ccs_bo,
@@ -532,7 +532,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
retval = xe_map_rd(xe, &ccs_bo->vmap, 0, u64);
check(retval, 0, "Clear ccs data first value", test);
retval = xe_map_rd(xe, &ccs_bo->vmap, ccs_bo->size - 8, u64);
retval = xe_map_rd(xe, &ccs_bo->vmap, xe_bo_size(ccs_bo) - 8, u64);
check(retval, 0, "Clear ccs data last value", test);
}
dma_fence_put(fence);
@@ -562,7 +562,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
retval = xe_map_rd(xe, &vram_bo->vmap, 0, u64);
check(retval, expected, "Restored value must be equal to initial value", test);
retval = xe_map_rd(xe, &vram_bo->vmap, vram_bo->size - 8, u64);
retval = xe_map_rd(xe, &vram_bo->vmap, xe_bo_size(vram_bo) - 8, u64);
check(retval, expected, "Restored value must be equal to initial value", test);
fence = blt_copy(tile, vram_bo, ccs_bo,
@@ -570,7 +570,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
if (!sanity_fence_failed(xe, fence, "Clear ccs buffer data", test)) {
retval = xe_map_rd(xe, &ccs_bo->vmap, 0, u64);
check(retval, 0, "Clear ccs data first value", test);
retval = xe_map_rd(xe, &ccs_bo->vmap, ccs_bo->size - 8, u64);
retval = xe_map_rd(xe, &ccs_bo->vmap, xe_bo_size(ccs_bo) - 8, u64);
check(retval, 0, "Clear ccs data last value", test);
}
dma_fence_put(fence);
@@ -583,7 +583,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
u64 expected, retval;
expected = 0xd0d0d0d0d0d0d0d0;
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, sys_bo->size);
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, xe_bo_size(sys_bo));
fence = blt_copy(tile, sys_bo, vram_bo, false, "Blit copy from sysmem to vram", test);
if (!sanity_fence_failed(xe, fence, "Blit copy from sysmem to vram", test)) {
@@ -597,7 +597,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
if (!sanity_fence_failed(xe, fence, "Blit copy from vram to sysmem", test)) {
retval = xe_map_rd(xe, &sys_bo->vmap, 0, u64);
check(retval, expected, "Decompressed value must be equal to initial value", test);
retval = xe_map_rd(xe, &sys_bo->vmap, sys_bo->size - 8, u64);
retval = xe_map_rd(xe, &sys_bo->vmap, xe_bo_size(sys_bo) - 8, u64);
check(retval, expected, "Decompressed value must be equal to initial value", test);
}
dma_fence_put(fence);
@@ -615,7 +615,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
if (!sanity_fence_failed(xe, fence, "Clear main buffer data", test)) {
retval = xe_map_rd(xe, &sys_bo->vmap, 0, u64);
check(retval, expected, "Clear main buffer first value", test);
retval = xe_map_rd(xe, &sys_bo->vmap, sys_bo->size - 8, u64);
retval = xe_map_rd(xe, &sys_bo->vmap, xe_bo_size(sys_bo) - 8, u64);
check(retval, expected, "Clear main buffer last value", test);
}
dma_fence_put(fence);
@@ -625,7 +625,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
if (!sanity_fence_failed(xe, fence, "Clear ccs buffer data", test)) {
retval = xe_map_rd(xe, &sys_bo->vmap, 0, u64);
check(retval, expected, "Clear ccs data first value", test);
retval = xe_map_rd(xe, &sys_bo->vmap, sys_bo->size - 8, u64);
retval = xe_map_rd(xe, &sys_bo->vmap, xe_bo_size(sys_bo) - 8, u64);
check(retval, expected, "Clear ccs data last value", test);
}
dma_fence_put(fence);
+31
View File
@@ -21,6 +21,18 @@ static void xe_ip_kunit_desc(const struct xe_ip *param, char *desc)
KUNIT_ARRAY_PARAM(graphics_ip, graphics_ips, xe_ip_kunit_desc);
KUNIT_ARRAY_PARAM(media_ip, media_ips, xe_ip_kunit_desc);
static void xe_pci_id_kunit_desc(const struct pci_device_id *param, char *desc)
{
const struct xe_device_desc *dev_desc =
(const struct xe_device_desc *)param->driver_data;
if (dev_desc)
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "0x%X (%s)",
param->device, dev_desc->platform_name);
}
KUNIT_ARRAY_PARAM(pci_id, pciidlist, xe_pci_id_kunit_desc);
/**
* xe_pci_graphics_ip_gen_param - Generate graphics struct xe_ip parameters
* @prev: the pointer to the previous parameter to iterate from or NULL
@@ -55,6 +67,25 @@ const void *xe_pci_media_ip_gen_param(const void *prev, char *desc)
}
EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param);
/**
* xe_pci_id_gen_param - Generate struct pci_device_id parameters
* @prev: the pointer to the previous parameter to iterate from or NULL
* @desc: output buffer with minimum size of KUNIT_PARAM_DESC_SIZE
*
* This function prepares struct pci_device_id parameter.
*
* To be used only as a parameter generator function in &KUNIT_CASE_PARAM.
*
* Return: pointer to the next parameter or NULL if no more parameters
*/
const void *xe_pci_id_gen_param(const void *prev, char *desc)
{
const struct pci_device_id *pci = pci_id_gen_params(prev, desc);
return pci->driver_data ? pci : NULL;
}
EXPORT_SYMBOL_IF_KUNIT(xe_pci_id_gen_param);
static void fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
u32 *ver, u32 *revid)
{
+12
View File
@@ -44,9 +44,21 @@ static void check_media_ip(struct kunit *test)
KUNIT_ASSERT_EQ(test, mask, 0);
}
static void check_platform_gt_count(struct kunit *test)
{
const struct pci_device_id *pci = test->param_value;
const struct xe_device_desc *desc =
(const struct xe_device_desc *)pci->driver_data;
int max_gt = desc->max_gt_per_tile;
KUNIT_ASSERT_GT(test, max_gt, 0);
KUNIT_ASSERT_LE(test, max_gt, XE_MAX_GT_PER_TILE);
}
static struct kunit_case xe_pci_tests[] = {
KUNIT_CASE_PARAM(check_graphics_ip, xe_pci_graphics_ip_gen_param),
KUNIT_CASE_PARAM(check_media_ip, xe_pci_media_ip_gen_param),
KUNIT_CASE_PARAM(check_platform_gt_count, xe_pci_id_gen_param),
{}
};
+1
View File
@@ -27,6 +27,7 @@ int xe_pci_fake_device_init(struct xe_device *xe);
const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc);
const void *xe_pci_media_ip_gen_param(const void *prev, char *desc);
const void *xe_pci_id_gen_param(const void *prev, char *desc);
const void *xe_pci_live_device_gen_param(const void *prev, char *desc);
#endif
+27 -10
View File
@@ -19,6 +19,8 @@
#include <kunit/static_stub.h>
#include <trace/events/gpu_mem.h>
#include "xe_device.h"
#include "xe_dma_buf.h"
#include "xe_drm_client.h"
@@ -418,6 +420,19 @@ static void xe_ttm_tt_account_subtract(struct xe_device *xe, struct ttm_tt *tt)
xe_shrinker_mod_pages(xe->mem.shrinker, -(long)tt->num_pages, 0);
}
static void update_global_total_pages(struct ttm_device *ttm_dev,
long num_pages)
{
#if IS_ENABLED(CONFIG_TRACE_GPU_MEM)
struct xe_device *xe = ttm_to_xe_device(ttm_dev);
u64 global_total_pages =
atomic64_add_return(num_pages, &xe->global_total_pages);
trace_gpu_mem_total(xe->drm.primary->index, 0,
global_total_pages << PAGE_SHIFT);
#endif
}
static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
u32 page_flags)
{
@@ -437,7 +452,7 @@ static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
extra_pages = 0;
if (xe_bo_needs_ccs_pages(bo))
extra_pages = DIV_ROUND_UP(xe_device_ccs_bytes(xe, bo->size),
extra_pages = DIV_ROUND_UP(xe_device_ccs_bytes(xe, xe_bo_size(bo)),
PAGE_SIZE);
/*
@@ -525,6 +540,7 @@ static int xe_ttm_tt_populate(struct ttm_device *ttm_dev, struct ttm_tt *tt,
xe_tt->purgeable = false;
xe_ttm_tt_account_add(ttm_to_xe_device(ttm_dev), tt);
update_global_total_pages(ttm_dev, tt->num_pages);
return 0;
}
@@ -541,6 +557,7 @@ static void xe_ttm_tt_unpopulate(struct ttm_device *ttm_dev, struct ttm_tt *tt)
ttm_pool_free(&ttm_dev->pool, tt);
xe_ttm_tt_account_subtract(xe, tt);
update_global_total_pages(ttm_dev, -(long)tt->num_pages);
}
static void xe_ttm_tt_destroy(struct ttm_device *ttm_dev, struct ttm_tt *tt)
@@ -1122,7 +1139,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo)
if (bo->flags & XE_BO_FLAG_PINNED_NORESTORE)
goto out_unlock_bo;
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, bo->size,
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED);
@@ -1200,7 +1217,8 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
goto out_unlock_bo;
if (!backup) {
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, bo->size,
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv,
NULL, xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED);
@@ -1254,7 +1272,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
}
xe_map_memcpy_from(xe, backup->vmap.vaddr, &bo->vmap, 0,
bo->size);
xe_bo_size(bo));
}
if (!bo->backup_obj)
@@ -1347,7 +1365,7 @@ int xe_bo_restore_pinned(struct xe_bo *bo)
}
xe_map_memcpy_to(xe, &bo->vmap, 0, backup->vmap.vaddr,
bo->size);
xe_bo_size(bo));
}
bo->backup_obj = NULL;
@@ -1558,7 +1576,7 @@ static int xe_ttm_access_memory(struct ttm_buffer_object *ttm_bo,
vram = res_to_mem_region(ttm_bo->resource);
xe_res_first(ttm_bo->resource, offset & PAGE_MASK,
bo->size - (offset & PAGE_MASK), &cursor);
xe_bo_size(bo) - (offset & PAGE_MASK), &cursor);
do {
unsigned long page_offset = (offset & ~PAGE_MASK);
@@ -1858,7 +1876,6 @@ struct xe_bo *___xe_bo_create_locked(struct xe_device *xe, struct xe_bo *bo,
bo->ccs_cleared = false;
bo->tile = tile;
bo->size = size;
bo->flags = flags;
bo->cpu_caching = cpu_caching;
bo->ttm.base.funcs = &xe_gem_object_funcs;
@@ -2036,7 +2053,7 @@ __xe_bo_create_locked(struct xe_device *xe,
if (flags & XE_BO_FLAG_FIXED_PLACEMENT) {
err = xe_ggtt_insert_bo_at(t->mem.ggtt, bo,
start + bo->size, U64_MAX);
start + xe_bo_size(bo), U64_MAX);
} else {
err = xe_ggtt_insert_bo(t->mem.ggtt, bo);
}
@@ -2234,7 +2251,7 @@ int xe_managed_bo_reinit_in_vram(struct xe_device *xe, struct xe_tile *tile, str
xe_assert(xe, !(*src)->vmap.is_iomem);
bo = xe_managed_bo_create_from_data(xe, tile, (*src)->vmap.vaddr,
(*src)->size, dst_flags);
xe_bo_size(*src), dst_flags);
if (IS_ERR(bo))
return PTR_ERR(bo);
@@ -2524,7 +2541,7 @@ int xe_bo_vmap(struct xe_bo *bo)
* TODO: Fix up ttm_bo_vmap to do that, or fix up ttm_bo_kmap
* to use struct iosys_map.
*/
ret = ttm_bo_kmap(&bo->ttm, 0, bo->size >> PAGE_SHIFT, &bo->kmap);
ret = ttm_bo_kmap(&bo->ttm, 0, xe_bo_size(bo) >> PAGE_SHIFT, &bo->kmap);
if (ret)
return ret;
+15 -2
View File
@@ -238,6 +238,19 @@ xe_bo_main_addr(struct xe_bo *bo, size_t page_size)
return xe_bo_addr(bo, 0, page_size);
}
/**
* xe_bo_size() - Xe BO size
* @bo: The bo object.
*
* Simple helper to return Xe BO's size.
*
* Return: Xe BO's size
*/
static inline size_t xe_bo_size(struct xe_bo *bo)
{
return bo->ttm.base.size;
}
static inline u32
__xe_bo_ggtt_addr(struct xe_bo *bo, u8 tile_id)
{
@@ -246,7 +259,7 @@ __xe_bo_ggtt_addr(struct xe_bo *bo, u8 tile_id)
if (XE_WARN_ON(!ggtt_node))
return 0;
XE_WARN_ON(ggtt_node->base.size > bo->size);
XE_WARN_ON(ggtt_node->base.size > xe_bo_size(bo));
XE_WARN_ON(ggtt_node->base.start + ggtt_node->base.size > (1ull << 32));
return ggtt_node->base.start;
}
@@ -300,7 +313,7 @@ bool xe_bo_needs_ccs_pages(struct xe_bo *bo);
static inline size_t xe_bo_ccs_pages_start(struct xe_bo *bo)
{
return PAGE_ALIGN(bo->ttm.base.size);
return PAGE_ALIGN(xe_bo_size(bo));
}
static inline bool xe_bo_has_pages(struct xe_bo *bo)
+1 -3
View File
@@ -32,8 +32,6 @@ struct xe_bo {
struct xe_bo *backup_obj;
/** @parent_obj: Ref to parent bo if this a backup_obj */
struct xe_bo *parent_obj;
/** @size: Size of this buffer object */
size_t size;
/** @flags: flags for this buffer object */
u32 flags;
/** @vm: VM this BO is attached to, for extobj this will be NULL */
@@ -86,7 +84,7 @@ struct xe_bo {
u16 cpu_caching;
/** @devmem_allocation: SVM device memory allocation */
struct drm_gpusvm_devmem devmem_allocation;
struct drm_pagemap_devmem devmem_allocation;
/** @vram_userfault_link: Link into @mem_access.vram_userfault.list */
struct list_head vram_userfault_link;
+28 -22
View File
@@ -171,14 +171,32 @@ static void xe_devcoredump_snapshot_free(struct xe_devcoredump_snapshot *ss)
#define XE_DEVCOREDUMP_CHUNK_MAX (SZ_512M + SZ_1G)
/**
* xe_devcoredump_read() - Read data from the Xe device coredump snapshot
* @buffer: Destination buffer to copy the coredump data into
* @offset: Offset in the coredump data to start reading from
* @count: Number of bytes to read
* @data: Pointer to the xe_devcoredump structure
* @datalen: Length of the data (unused)
*
* Reads a chunk of the coredump snapshot data into the provided buffer.
* If the devcoredump is smaller than 1.5 GB (XE_DEVCOREDUMP_CHUNK_MAX),
* it is read directly from a pre-written buffer. For larger devcoredumps,
* the pre-written buffer must be periodically repopulated from the snapshot
* state due to kmalloc size limitations.
*
* Return: Number of bytes copied on success, or a negative error code on failure.
*/
static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
size_t count, void *data, size_t datalen)
{
struct xe_devcoredump *coredump = data;
struct xe_devcoredump_snapshot *ss;
ssize_t byte_copied;
ssize_t byte_copied = 0;
u32 chunk_offset;
ssize_t new_chunk_position;
bool pm_needed = false;
int ret = 0;
if (!coredump)
return -ENODEV;
@@ -188,20 +206,19 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
/* Ensure delayed work is captured before continuing */
flush_work(&ss->work);
if (ss->read.size > XE_DEVCOREDUMP_CHUNK_MAX)
pm_needed = ss->read.size > XE_DEVCOREDUMP_CHUNK_MAX;
if (pm_needed)
xe_pm_runtime_get(gt_to_xe(ss->gt));
mutex_lock(&coredump->lock);
if (!ss->read.buffer) {
mutex_unlock(&coredump->lock);
return -ENODEV;
ret = -ENODEV;
goto unlock;
}
if (offset >= ss->read.size) {
mutex_unlock(&coredump->lock);
return 0;
}
if (offset >= ss->read.size)
goto unlock;
new_chunk_position = div_u64_rem(offset,
XE_DEVCOREDUMP_CHUNK_MAX,
@@ -221,12 +238,13 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
ss->read.size - offset;
memcpy(buffer, ss->read.buffer + chunk_offset, byte_copied);
unlock:
mutex_unlock(&coredump->lock);
if (ss->read.size > XE_DEVCOREDUMP_CHUNK_MAX)
if (pm_needed)
xe_pm_runtime_put(gt_to_xe(ss->gt));
return byte_copied;
return byte_copied ? byte_copied : ret;
}
static void xe_devcoredump_free(void *data)
@@ -313,13 +331,9 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
{
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
struct xe_guc *guc = exec_queue_to_guc(q);
u32 adj_logical_mask = q->logical_mask;
u32 width_mask = (0x1 << q->width) - 1;
const char *process_name = "no process";
unsigned int fw_ref;
bool cookie;
int i;
ss->snapshot_time = ktime_get_real();
ss->boot_time = ktime_get_boottime();
@@ -335,14 +349,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
cookie = dma_fence_begin_signalling();
for (i = 0; q->width > 1 && i < XE_HW_ENGINE_MAX_INSTANCE;) {
if (adj_logical_mask & BIT(i)) {
adj_logical_mask |= width_mask << i;
i += q->width;
} else {
++i;
}
}
/* keep going if fw fails as we still want to save the memory and SW data */
fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
+62 -59
View File
@@ -40,12 +40,14 @@
#include "xe_gt_printk.h"
#include "xe_gt_sriov_vf.h"
#include "xe_guc.h"
#include "xe_guc_pc.h"
#include "xe_hw_engine_group.h"
#include "xe_hwmon.h"
#include "xe_i2c.h"
#include "xe_irq.h"
#include "xe_memirq.h"
#include "xe_mmio.h"
#include "xe_module.h"
#include "xe_nvm.h"
#include "xe_oa.h"
#include "xe_observation.h"
#include "xe_pat.h"
@@ -783,46 +785,16 @@ int xe_device_probe(struct xe_device *xe)
if (err)
return err;
err = xe_ttm_sys_mgr_init(xe);
if (err)
return err;
for_each_gt(gt, xe, id) {
err = xe_gt_init_early(gt);
if (err)
return err;
/*
* Only after this point can GT-specific MMIO operations
* (including things like communication with the GuC)
* be performed.
*/
xe_gt_mmio_init(gt);
if (IS_SRIOV_VF(xe)) {
xe_guc_comm_init_early(&gt->uc.guc);
err = xe_gt_sriov_vf_bootstrap(gt);
if (err)
return err;
err = xe_gt_sriov_vf_query_config(gt);
if (err)
return err;
}
}
for_each_tile(tile, xe, id) {
err = xe_ggtt_init_early(tile->mem.ggtt);
if (err)
return err;
err = xe_memirq_init(&tile->memirq);
if (err)
return err;
}
for_each_gt(gt, xe, id) {
err = xe_gt_init_hwconfig(gt);
if (err)
return err;
}
err = xe_devcoredump_init(xe);
@@ -850,6 +822,14 @@ int xe_device_probe(struct xe_device *xe)
return err;
}
/*
* Allow allocations only now to ensure xe_display_init_early()
* is the first to allocate, always.
*/
err = xe_ttm_sys_mgr_init(xe);
if (err)
return err;
/* Allocate and map stolen after potential VRAM resize */
err = xe_ttm_stolen_mgr_init(xe);
if (err)
@@ -881,6 +861,8 @@ int xe_device_probe(struct xe_device *xe)
return err;
}
xe_nvm_init(xe);
err = xe_heci_gsc_init(xe);
if (err)
return err;
@@ -921,6 +903,10 @@ int xe_device_probe(struct xe_device *xe)
if (err)
goto err_unregister_display;
err = xe_i2c_probe(xe);
if (err)
goto err_unregister_display;
for_each_gt(gt, xe, id)
xe_gt_sanitize_freq(gt);
@@ -938,6 +924,8 @@ void xe_device_remove(struct xe_device *xe)
{
xe_display_unregister(xe);
xe_nvm_fini(xe);
drm_dev_unplug(&xe->drm);
xe_bo_pci_dev_remove_all(xe);
@@ -981,38 +969,15 @@ void xe_device_wmb(struct xe_device *xe)
xe_mmio_write32(xe_root_tile_mmio(xe), VF_CAP_REG, 0);
}
/**
* xe_device_td_flush() - Flush transient L3 cache entries
* @xe: The device
*
* Display engine has direct access to memory and is never coherent with L3/L4
* caches (or CPU caches), however KMD is responsible for specifically flushing
* transient L3 GPU cache entries prior to the flip sequence to ensure scanout
* can happen from such a surface without seeing corruption.
*
* Display surfaces can be tagged as transient by mapping it using one of the
* various L3:XD PAT index modes on Xe2.
*
* Note: On non-discrete xe2 platforms, like LNL, the entire L3 cache is flushed
* at the end of each submission via PIPE_CONTROL for compute/render, since SA
* Media is not coherent with L3 and we want to support render-vs-media
* usescases. For other engines like copy/blt the HW internally forces uncached
* behaviour, hence why we can skip the TDF on such platforms.
/*
* Issue a TRANSIENT_FLUSH_REQUEST and wait for completion on each gt.
*/
void xe_device_td_flush(struct xe_device *xe)
static void tdf_request_sync(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int fw_ref;
struct xe_gt *gt;
u8 id;
if (!IS_DGFX(xe) || GRAPHICS_VER(xe) < 20)
return;
if (XE_WA(xe_root_mmio_gt(xe), 16023588340)) {
xe_device_l2_flush(xe);
return;
}
for_each_gt(gt, xe, id) {
if (xe_gt_is_media_type(gt))
continue;
@@ -1022,6 +987,7 @@ void xe_device_td_flush(struct xe_device *xe)
return;
xe_mmio_write32(&gt->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST);
/*
* FIXME: We can likely do better here with our choice of
* timeout. Currently we just assume the worst case, i.e. 150us,
@@ -1052,15 +1018,52 @@ void xe_device_l2_flush(struct xe_device *xe)
return;
spin_lock(&gt->global_invl_lock);
xe_mmio_write32(&gt->mmio, XE2_GLOBAL_INVAL, 0x1);
xe_mmio_write32(&gt->mmio, XE2_GLOBAL_INVAL, 0x1);
if (xe_mmio_wait32(&gt->mmio, XE2_GLOBAL_INVAL, 0x1, 0x0, 500, NULL, true))
xe_gt_err_once(gt, "Global invalidation timeout\n");
spin_unlock(&gt->global_invl_lock);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
}
/**
* xe_device_td_flush() - Flush transient L3 cache entries
* @xe: The device
*
* Display engine has direct access to memory and is never coherent with L3/L4
* caches (or CPU caches), however KMD is responsible for specifically flushing
* transient L3 GPU cache entries prior to the flip sequence to ensure scanout
* can happen from such a surface without seeing corruption.
*
* Display surfaces can be tagged as transient by mapping it using one of the
* various L3:XD PAT index modes on Xe2.
*
* Note: On non-discrete xe2 platforms, like LNL, the entire L3 cache is flushed
* at the end of each submission via PIPE_CONTROL for compute/render, since SA
* Media is not coherent with L3 and we want to support render-vs-media
* usescases. For other engines like copy/blt the HW internally forces uncached
* behaviour, hence why we can skip the TDF on such platforms.
*/
void xe_device_td_flush(struct xe_device *xe)
{
struct xe_gt *root_gt;
if (!IS_DGFX(xe) || GRAPHICS_VER(xe) < 20)
return;
root_gt = xe_root_mmio_gt(xe);
if (XE_WA(root_gt, 16023588340)) {
/* A transient flush is not sufficient: flush the L2 */
xe_device_l2_flush(xe);
} else {
xe_guc_pc_apply_flush_freq_limit(&root_gt->uc.guc.pc);
tdf_request_sync(xe);
xe_guc_pc_remove_flush_freq_limit(&root_gt->uc.guc.pc);
}
}
u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size)
{
return xe_device_has_flat_ccs(xe) ?
+20 -27
View File
@@ -60,35 +60,32 @@ static inline struct xe_tile *xe_device_get_root_tile(struct xe_device *xe)
return &xe->tiles[0];
}
/*
* Highest GT/tile count for any platform. Used only for memory allocation
* sizing. Any logic looping over GTs or mapping userspace GT IDs into GT
* structures should use the per-platform xe->info.max_gt_per_tile instead.
*/
#define XE_MAX_GT_PER_TILE 2
static inline struct xe_gt *xe_tile_get_gt(struct xe_tile *tile, u8 gt_id)
{
if (drm_WARN_ON(&tile_to_xe(tile)->drm, gt_id >= XE_MAX_GT_PER_TILE))
gt_id = 0;
return gt_id ? tile->media_gt : tile->primary_gt;
}
static inline struct xe_gt *xe_device_get_gt(struct xe_device *xe, u8 gt_id)
{
struct xe_tile *root_tile = xe_device_get_root_tile(xe);
struct xe_tile *tile;
struct xe_gt *gt;
/*
* FIXME: This only works for now because multi-tile and standalone
* media are mutually exclusive on the platforms we have today.
*
* id => GT mapping may change once we settle on how we want to handle
* our UAPI.
*/
if (MEDIA_VER(xe) >= 13) {
gt = xe_tile_get_gt(root_tile, gt_id);
} else {
if (drm_WARN_ON(&xe->drm, gt_id >= XE_MAX_TILES_PER_DEVICE))
gt_id = 0;
if (gt_id >= xe->info.tile_count * xe->info.max_gt_per_tile)
return NULL;
gt = xe->tiles[gt_id].primary_gt;
tile = &xe->tiles[gt_id / xe->info.max_gt_per_tile];
switch (gt_id % xe->info.max_gt_per_tile) {
default:
xe_assert(xe, false);
fallthrough;
case 0:
gt = tile->primary_gt;
break;
case 1:
gt = tile->media_gt;
break;
}
if (!gt)
@@ -130,12 +127,8 @@ static inline bool xe_device_uc_enabled(struct xe_device *xe)
for ((id__) = 1; (id__) < (xe__)->info.tile_count; (id__)++) \
for_each_if((tile__) = &(xe__)->tiles[(id__)])
/*
* FIXME: This only works for now since multi-tile and standalone media
* happen to be mutually exclusive. Future platforms may change this...
*/
#define for_each_gt(gt__, xe__, id__) \
for ((id__) = 0; (id__) < (xe__)->info.gt_count; (id__)++) \
for ((id__) = 0; (id__) < (xe__)->info.tile_count * (xe__)->info.max_gt_per_tile; (id__)++) \
for_each_if((gt__) = xe_device_get_gt((xe__), (id__)))
static inline struct xe_force_wake *gt_to_fw(struct xe_gt *gt)
+142 -1
View File
@@ -24,6 +24,12 @@
*
* vram_d3cold_threshold - Report/change vram used threshold(in MB) below
* which vram save/restore is permissible during runtime D3cold entry/exit.
*
* lb_fan_control_version - Fan control version provisioned by late binding.
* Exposed only if supported by the device.
*
* lb_voltage_regulator_version - Voltage regulator version provisioned by late
* binding. Exposed only if supported by the device.
*/
static ssize_t
@@ -65,6 +71,135 @@ vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(vram_d3cold_threshold);
static ssize_t
lb_fan_control_version_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
struct xe_tile *root = xe_device_get_root_tile(xe);
u32 cap, ver_low = FAN_TABLE, ver_high = FAN_TABLE;
u16 major = 0, minor = 0, hotfix = 0, build = 0;
int ret;
xe_pm_runtime_get(xe);
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
if (ret)
goto out;
if (REG_FIELD_GET(V1_FAN_PROVISIONED, cap)) {
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
&ver_low, NULL);
if (ret)
goto out;
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
&ver_high, NULL);
if (ret)
goto out;
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
}
out:
xe_pm_runtime_put(xe);
return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
}
static DEVICE_ATTR_ADMIN_RO(lb_fan_control_version);
static ssize_t
lb_voltage_regulator_version_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
struct xe_tile *root = xe_device_get_root_tile(xe);
u32 cap, ver_low = VR_CONFIG, ver_high = VR_CONFIG;
u16 major = 0, minor = 0, hotfix = 0, build = 0;
int ret;
xe_pm_runtime_get(xe);
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
if (ret)
goto out;
if (REG_FIELD_GET(VR_PARAMS_PROVISIONED, cap)) {
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
&ver_low, NULL);
if (ret)
goto out;
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
&ver_high, NULL);
if (ret)
goto out;
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
}
out:
xe_pm_runtime_put(xe);
return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
}
static DEVICE_ATTR_ADMIN_RO(lb_voltage_regulator_version);
static int late_bind_create_files(struct device *dev)
{
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
struct xe_tile *root = xe_device_get_root_tile(xe);
u32 cap;
int ret;
xe_pm_runtime_get(xe);
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
if (ret)
goto out;
if (REG_FIELD_GET(V1_FAN_SUPPORTED, cap)) {
ret = sysfs_create_file(&dev->kobj, &dev_attr_lb_fan_control_version.attr);
if (ret)
goto out;
}
if (REG_FIELD_GET(VR_PARAMS_SUPPORTED, cap))
ret = sysfs_create_file(&dev->kobj, &dev_attr_lb_voltage_regulator_version.attr);
out:
xe_pm_runtime_put(xe);
return ret;
}
static void late_bind_remove_files(struct device *dev)
{
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
struct xe_tile *root = xe_device_get_root_tile(xe);
u32 cap;
int ret;
xe_pm_runtime_get(xe);
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
if (ret)
goto out;
if (REG_FIELD_GET(V1_FAN_SUPPORTED, cap))
sysfs_remove_file(&dev->kobj, &dev_attr_lb_fan_control_version.attr);
if (REG_FIELD_GET(VR_PARAMS_SUPPORTED, cap))
sysfs_remove_file(&dev->kobj, &dev_attr_lb_voltage_regulator_version.attr);
out:
xe_pm_runtime_put(xe);
}
/**
* DOC: PCIe Gen5 Limitations
*
@@ -151,8 +286,10 @@ static void xe_device_sysfs_fini(void *arg)
if (xe->d3cold.capable)
sysfs_remove_file(&xe->drm.dev->kobj, &dev_attr_vram_d3cold_threshold.attr);
if (xe->info.platform == XE_BATTLEMAGE)
if (xe->info.platform == XE_BATTLEMAGE) {
sysfs_remove_files(&xe->drm.dev->kobj, auto_link_downgrade_attrs);
late_bind_remove_files(xe->drm.dev);
}
}
int xe_device_sysfs_init(struct xe_device *xe)
@@ -170,6 +307,10 @@ int xe_device_sysfs_init(struct xe_device *xe)
ret = sysfs_create_files(&dev->kobj, auto_link_downgrade_attrs);
if (ret)
return ret;
ret = late_bind_create_files(dev);
if (ret)
return ret;
}
return devm_add_action_or_reset(dev, xe_device_sysfs_fini, xe);
+21 -1
View File
@@ -32,7 +32,9 @@
struct dram_info;
struct intel_display;
struct intel_dg_nvm_dev;
struct xe_ggtt;
struct xe_i2c;
struct xe_pat_ops;
struct xe_pxp;
@@ -105,7 +107,7 @@ struct xe_vram_region {
void __iomem *mapping;
/** @ttm: VRAM TTM manager */
struct xe_ttm_vram_mgr ttm;
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
/** @pagemap: Used to remap device memory as ZONE_DEVICE */
struct dev_pagemap pagemap;
/**
@@ -293,6 +295,8 @@ struct xe_device {
u8 vram_flags;
/** @info.tile_count: Number of tiles */
u8 tile_count;
/** @info.max_gt_per_tile: Number of GT IDs allocated to each tile */
u8 max_gt_per_tile;
/** @info.gt_count: Total number of GTs for entire device */
u8 gt_count;
/** @info.vm_max_level: Max VM level */
@@ -316,6 +320,8 @@ struct xe_device {
u8 has_fan_control:1;
/** @info.has_flat_ccs: Whether flat CCS metadata is used */
u8 has_flat_ccs:1;
/** @info.has_gsc_nvm: Device has gsc non-volatile memory */
u8 has_gsc_nvm:1;
/** @info.has_heci_cscfi: device has heci cscfi */
u8 has_heci_cscfi:1;
/** @info.has_heci_gscfi: device has heci gscfi */
@@ -549,6 +555,9 @@ struct xe_device {
/** @heci_gsc: graphics security controller */
struct xe_heci_gsc heci_gsc;
/** @nvm: discrete graphics non-volatile memory */
struct intel_dg_nvm_dev *nvm;
/** @oa: oa observation subsystem */
struct xe_oa oa;
@@ -577,6 +586,9 @@ struct xe_device {
/** @pmu: performance monitoring unit */
struct xe_pmu pmu;
/** @i2c: I2C host controller */
struct xe_i2c *i2c;
/** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */
u32 atomic_svm_timeslice_ms;
@@ -588,6 +600,14 @@ struct xe_device {
u8 vm_inject_error_position;
#endif
#if IS_ENABLED(CONFIG_TRACE_GPU_MEM)
/**
* @global_total_pages: global GPU page usage tracked for gpu_mem
* tracepoints
*/
atomic64_t global_total_pages;
#endif
/* private: */
#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
+1 -1
View File
@@ -167,7 +167,7 @@ void xe_drm_client_remove_bo(struct xe_bo *bo)
static void bo_meminfo(struct xe_bo *bo,
struct drm_memory_stats stats[TTM_NUM_MEM_TYPES])
{
u64 sz = bo->size;
u64 sz = xe_bo_size(bo);
u32 mem_type = bo->ttm.resource->mem_type;
xe_bo_assert_held(bo);
+4 -2
View File
@@ -258,11 +258,13 @@ static int set_prop_eu_stall_wait_num_reports(struct xe_device *xe, u64 value,
static int set_prop_eu_stall_gt_id(struct xe_device *xe, u64 value,
struct eu_stall_open_properties *props)
{
if (value >= xe->info.gt_count) {
struct xe_gt *gt = xe_device_get_gt(xe, value);
if (!gt) {
drm_dbg(&xe->drm, "Invalid GT ID %llu for EU stall sampling\n", value);
return -EINVAL;
}
props->gt = xe_device_get_gt(xe, value);
props->gt = gt;
return 0;
}
+1 -1
View File
@@ -610,7 +610,7 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
if (XE_IOCTL_DBG(xe, err))
return -EFAULT;
if (XE_IOCTL_DBG(xe, eci[0].gt_id >= xe->info.gt_count))
if (XE_IOCTL_DBG(xe, !xe_device_get_gt(xe, eci[0].gt_id)))
return -EINVAL;
if (args->flags & DRM_XE_EXEC_QUEUE_LOW_LATENCY_HINT)
+18 -7
View File
@@ -238,6 +238,13 @@ int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size)
}
EXPORT_SYMBOL_IF_KUNIT(xe_ggtt_init_kunit);
static void dev_fini_ggtt(void *arg)
{
struct xe_ggtt *ggtt = arg;
drain_workqueue(ggtt->wq);
}
/**
* xe_ggtt_init_early - Early GGTT initialization
* @ggtt: the &xe_ggtt to be initialized
@@ -290,6 +297,10 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
if (err)
return err;
err = devm_add_action_or_reset(xe->drm.dev, dev_fini_ggtt, ggtt);
if (err)
return err;
if (IS_SRIOV_VF(xe)) {
err = xe_tile_sriov_vf_prepare_ggtt(ggtt->tile);
if (err)
@@ -410,7 +421,7 @@ int xe_ggtt_init(struct xe_ggtt *ggtt)
goto err;
}
xe_map_memset(xe, &ggtt->scratch->vmap, 0, 0, ggtt->scratch->size);
xe_map_memset(xe, &ggtt->scratch->vmap, 0, 0, xe_bo_size(ggtt->scratch));
xe_ggtt_initial_clear(ggtt);
@@ -682,13 +693,13 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
return;
start = node->base.start;
end = start + bo->size;
end = start + xe_bo_size(bo);
pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index);
if (!xe_bo_is_vram(bo) && !xe_bo_is_stolen(bo)) {
xe_assert(xe_bo_device(bo), bo->ttm.ttm);
for (xe_res_first_sg(xe_bo_sg(bo), 0, bo->size, &cur);
for (xe_res_first_sg(xe_bo_sg(bo), 0, xe_bo_size(bo), &cur);
cur.remaining; xe_res_next(&cur, XE_PAGE_SIZE))
ggtt->pt_ops->ggtt_set_pte(ggtt, end - cur.remaining,
pte | xe_res_dma(&cur));
@@ -696,7 +707,7 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
/* Prepend GPU offset */
pte |= vram_region_gpu_offset(bo->ttm.resource);
for (xe_res_first(bo->ttm.resource, 0, bo->size, &cur);
for (xe_res_first(bo->ttm.resource, 0, xe_bo_size(bo), &cur);
cur.remaining; xe_res_next(&cur, XE_PAGE_SIZE))
ggtt->pt_ops->ggtt_set_pte(ggtt, end - cur.remaining,
pte + cur.start);
@@ -732,7 +743,7 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
if (XE_WARN_ON(bo->ggtt_node[tile_id])) {
/* Someone's already inserted this BO in the GGTT */
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == bo->size);
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == xe_bo_size(bo));
return 0;
}
@@ -751,7 +762,7 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
mutex_lock(&ggtt->lock);
err = drm_mm_insert_node_in_range(&ggtt->mm, &bo->ggtt_node[tile_id]->base,
bo->size, alignment, 0, start, end, 0);
xe_bo_size(bo), alignment, 0, start, end, 0);
if (err) {
xe_ggtt_node_fini(bo->ggtt_node[tile_id]);
bo->ggtt_node[tile_id] = NULL;
@@ -812,7 +823,7 @@ void xe_ggtt_remove_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
return;
/* This BO is not currently in the GGTT */
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == bo->size);
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == xe_bo_size(bo));
xe_ggtt_node_remove(bo->ggtt_node[tile_id],
bo->flags & XE_BO_FLAG_GGTT_INVALIDATE);
+4 -2
View File
@@ -59,7 +59,8 @@ static int memcpy_fw(struct xe_gsc *gsc)
xe_map_memcpy_from(xe, storage, &gsc->fw.bo->vmap, 0, fw_size);
xe_map_memcpy_to(xe, &gsc->private->vmap, 0, storage, fw_size);
xe_map_memset(xe, &gsc->private->vmap, fw_size, 0, gsc->private->size - fw_size);
xe_map_memset(xe, &gsc->private->vmap, fw_size, 0,
xe_bo_size(gsc->private) - fw_size);
kfree(storage);
@@ -82,7 +83,8 @@ static int emit_gsc_upload(struct xe_gsc *gsc)
bb->cs[bb->len++] = GSC_FW_LOAD;
bb->cs[bb->len++] = lower_32_bits(offset);
bb->cs[bb->len++] = upper_32_bits(offset);
bb->cs[bb->len++] = (gsc->private->size / SZ_4K) | GSC_FW_LOAD_LIMIT_VALID;
bb->cs[bb->len++] = (xe_bo_size(gsc->private) / SZ_4K) |
GSC_FW_LOAD_LIMIT_VALID;
job = xe_bb_create_job(gsc->q, bb);
if (IS_ERR(job)) {
+38 -42
View File
@@ -390,6 +390,7 @@ put_exec_queue:
int xe_gt_init_early(struct xe_gt *gt)
{
unsigned int fw_ref;
int err;
if (IS_SRIOV_PF(gt_to_xe(gt))) {
@@ -419,6 +420,25 @@ int xe_gt_init_early(struct xe_gt *gt)
xe_mocs_init_early(gt);
/*
* Only after this point can GT-specific MMIO operations
* (including things like communication with the GuC)
* be performed.
*/
xe_gt_mmio_init(gt);
err = xe_uc_init_noalloc(&gt->uc);
if (err)
return err;
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
return -ETIMEDOUT;
xe_gt_mcr_init_early(gt);
xe_pat_init(gt);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
return 0;
}
@@ -433,7 +453,7 @@ static void dump_pat_on_error(struct xe_gt *gt)
xe_pat_dump(gt, &p);
}
static int gt_fw_domain_init(struct xe_gt *gt)
static int gt_init_with_gt_forcewake(struct xe_gt *gt)
{
unsigned int fw_ref;
int err;
@@ -442,6 +462,14 @@ static int gt_fw_domain_init(struct xe_gt *gt)
if (!fw_ref)
return -ETIMEDOUT;
err = xe_uc_init(&gt->uc);
if (err)
goto err_force_wake;
xe_gt_topology_init(gt);
xe_gt_mcr_init(gt);
xe_gt_enable_host_l2_vram(gt);
if (!xe_gt_is_media_type(gt)) {
err = xe_ggtt_init(gt_to_tile(gt)->mem.ggtt);
if (err)
@@ -457,8 +485,10 @@ static int gt_fw_domain_init(struct xe_gt *gt)
xe_gt_mcr_init(gt);
err = xe_hw_engines_init_early(gt);
if (err)
if (err) {
dump_pat_on_error(gt);
goto err_force_wake;
}
err = xe_hw_engine_class_sysfs_init(gt);
if (err)
@@ -479,13 +509,12 @@ static int gt_fw_domain_init(struct xe_gt *gt)
return 0;
err_force_wake:
dump_pat_on_error(gt);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
return err;
}
static int all_fw_domain_init(struct xe_gt *gt)
static int gt_init_with_all_forcewake(struct xe_gt *gt)
{
unsigned int fw_ref;
int err;
@@ -544,7 +573,7 @@ static int all_fw_domain_init(struct xe_gt *gt)
}
}
err = xe_uc_init_hw(&gt->uc);
err = xe_uc_load_hw(&gt->uc);
if (err)
goto err_force_wake;
@@ -572,39 +601,6 @@ err_force_wake:
return err;
}
/*
* Initialize enough GT to be able to load GuC in order to obtain hwconfig and
* enable CTB communication.
*/
int xe_gt_init_hwconfig(struct xe_gt *gt)
{
unsigned int fw_ref;
int err;
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
return -ETIMEDOUT;
xe_gt_mcr_init_early(gt);
xe_pat_init(gt);
err = xe_uc_init(&gt->uc);
if (err)
goto out_fw;
err = xe_uc_init_hwconfig(&gt->uc);
if (err)
goto out_fw;
xe_gt_topology_init(gt);
xe_gt_mcr_init(gt);
xe_gt_enable_host_l2_vram(gt);
out_fw:
xe_force_wake_put(gt_to_fw(gt), fw_ref);
return err;
}
static void xe_gt_fini(void *arg)
{
struct xe_gt *gt = arg;
@@ -640,7 +636,7 @@ int xe_gt_init(struct xe_gt *gt)
if (err)
return err;
err = gt_fw_domain_init(gt);
err = gt_init_with_gt_forcewake(gt);
if (err)
return err;
@@ -654,7 +650,7 @@ int xe_gt_init(struct xe_gt *gt)
xe_force_wake_init_engines(gt, gt_to_fw(gt));
err = all_fw_domain_init(gt);
err = gt_init_with_all_forcewake(gt);
if (err)
return err;
@@ -742,7 +738,7 @@ static int vf_gt_restart(struct xe_gt *gt)
if (err)
return err;
err = xe_uc_init_hw(&gt->uc);
err = xe_uc_load_hw(&gt->uc);
if (err)
return err;
@@ -780,7 +776,7 @@ static int do_gt_restart(struct xe_gt *gt)
if (err)
return err;
err = xe_uc_init_hw(&gt->uc);
err = xe_uc_load_hw(&gt->uc);
if (err)
return err;
-1
View File
@@ -28,7 +28,6 @@ static inline bool xe_fault_inject_gt_reset(void)
}
struct xe_gt *xe_gt_alloc(struct xe_tile *tile);
int xe_gt_init_hwconfig(struct xe_gt *gt);
int xe_gt_init_early(struct xe_gt *gt);
int xe_gt_init(struct xe_gt *gt);
void xe_gt_mmio_init(struct xe_gt *gt);
+27 -20
View File
@@ -420,12 +420,6 @@ static void init_steering_sqidi_psmi(struct xe_gt *gt)
gt->steering[SQIDI_PSMI].instance_target = select & 0x1;
}
static void init_steering_inst0(struct xe_gt *gt)
{
gt->steering[INSTANCE0].group_target = 0; /* unused */
gt->steering[INSTANCE0].instance_target = 0; /* unused */
}
static const struct {
const char *name;
void (*init)(struct xe_gt *gt);
@@ -436,7 +430,7 @@ static const struct {
[DSS] = { "DSS", init_steering_dss },
[OADDRM] = { "OADDRM / GPMXMT", init_steering_oaddrm },
[SQIDI_PSMI] = { "SQIDI_PSMI", init_steering_sqidi_psmi },
[INSTANCE0] = { "INSTANCE 0", init_steering_inst0 },
[INSTANCE0] = { "INSTANCE 0", NULL },
[IMPLICIT_STEERING] = { "IMPLICIT", NULL },
};
@@ -446,25 +440,17 @@ static const struct {
*
* Perform early software only initialization of the MCR lock to allow
* the synchronization on accessing the STEER_SEMAPHORE register and
* use the xe_gt_mcr_multicast_write() function.
* use the xe_gt_mcr_multicast_write() function, plus the minimum
* safe MCR registers required for VRAM/CCS probing.
*/
void xe_gt_mcr_init_early(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
BUILD_BUG_ON(IMPLICIT_STEERING + 1 != NUM_STEERING_TYPES);
BUILD_BUG_ON(ARRAY_SIZE(xe_steering_types) != NUM_STEERING_TYPES);
spin_lock_init(&gt->mcr_lock);
}
/**
* xe_gt_mcr_init - Normal initialization of the MCR support
* @gt: GT structure
*
* Perform normal initialization of the MCR for all usages.
*/
void xe_gt_mcr_init(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
if (IS_SRIOV_VF(xe))
return;
@@ -505,10 +491,27 @@ void xe_gt_mcr_init(struct xe_gt *gt)
}
}
/* Mark instance 0 as initialized, we need this early for VRAM and CCS probe. */
gt->steering[INSTANCE0].initialized = true;
}
/**
* xe_gt_mcr_init - Normal initialization of the MCR support
* @gt: GT structure
*
* Perform normal initialization of the MCR for all usages.
*/
void xe_gt_mcr_init(struct xe_gt *gt)
{
if (IS_SRIOV_VF(gt_to_xe(gt)))
return;
/* Select non-terminated steering target for each type */
for (int i = 0; i < NUM_STEERING_TYPES; i++)
for (int i = 0; i < NUM_STEERING_TYPES; i++) {
gt->steering[i].initialized = true;
if (gt->steering[i].ranges && xe_steering_types[i].init)
xe_steering_types[i].init(gt);
}
}
/**
@@ -570,6 +573,10 @@ bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt,
for (int i = 0; gt->steering[type].ranges[i].end > 0; i++) {
if (xe_mmio_in_range(&gt->mmio, &gt->steering[type].ranges[i], reg)) {
drm_WARN(&gt_to_xe(gt)->drm, !gt->steering[type].initialized,
"Uninitialized usage of MCR register %s/%#x\n",
xe_steering_types[type].name, reg.addr);
*group = gt->steering[type].group_target;
*instance = gt->steering[type].instance_target;
return true;
+1
View File
@@ -419,6 +419,7 @@ static int xe_alloc_pf_queue(struct xe_gt *gt, struct pf_queue *pf_queue)
#define PF_MULTIPLIER 8
pf_queue->num_dw =
(num_eus + XE_NUM_HW_ENGINES) * PF_MSG_LEN_DW * PF_MULTIPLIER;
pf_queue->num_dw = roundup_pow_of_two(pf_queue->num_dw);
#undef PF_MULTIPLIER
pf_queue->gt = gt;
+13 -13
View File
@@ -104,13 +104,13 @@ static int pf_push_vf_buf_klvs(struct xe_gt *gt, unsigned int vfid, u32 num_klvs
}
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
struct drm_printer p = xe_gt_info_printer(gt);
struct drm_printer p = xe_gt_dbg_printer(gt);
void *klvs = xe_guc_buf_cpu_ptr(buf);
char name[8];
xe_gt_sriov_info(gt, "pushed %s config with %u KLV%s:\n",
xe_sriov_function_name(vfid, name, sizeof(name)),
num_klvs, str_plural(num_klvs));
xe_gt_sriov_dbg(gt, "pushed %s config with %u KLV%s:\n",
xe_sriov_function_name(vfid, name, sizeof(name)),
num_klvs, str_plural(num_klvs));
xe_guc_klv_print(klvs, num_dwords, &p);
}
@@ -282,8 +282,8 @@ static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool
if (config->lmem_obj) {
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_LMEM_SIZE);
cfg[n++] = lower_32_bits(config->lmem_obj->size);
cfg[n++] = upper_32_bits(config->lmem_obj->size);
cfg[n++] = lower_32_bits(xe_bo_size(config->lmem_obj));
cfg[n++] = upper_32_bits(xe_bo_size(config->lmem_obj));
}
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_EXEC_QUANTUM);
@@ -1299,7 +1299,7 @@ static u64 pf_get_vf_config_lmem(struct xe_gt *gt, unsigned int vfid)
struct xe_bo *bo;
bo = config->lmem_obj;
return bo ? bo->size : 0;
return bo ? xe_bo_size(bo) : 0;
}
static int pf_distribute_config_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
@@ -1388,7 +1388,7 @@ static int pf_update_vf_lmtt(struct xe_device *xe, unsigned int vfid)
err = xe_lmtt_populate_pages(lmtt, vfid, bo, offset);
if (err)
goto fail;
offset += bo->size;
offset += xe_bo_size(bo);
}
}
@@ -1469,12 +1469,12 @@ static int pf_provision_vf_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
goto release;
}
err = pf_push_vf_cfg_lmem(gt, vfid, bo->size);
err = pf_push_vf_cfg_lmem(gt, vfid, xe_bo_size(bo));
if (unlikely(err))
goto reset_lmtt;
xe_gt_sriov_dbg_verbose(gt, "VF%u LMEM %zu (%zuM)\n",
vfid, bo->size, bo->size / SZ_1M);
vfid, xe_bo_size(bo), xe_bo_size(bo) / SZ_1M);
return 0;
reset_lmtt:
@@ -2349,7 +2349,7 @@ int xe_gt_sriov_pf_config_restore(struct xe_gt *gt, unsigned int vfid,
return -EINVAL;
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
struct drm_printer p = xe_gt_info_printer(gt);
struct drm_printer p = xe_gt_dbg_printer(gt);
drm_printf(&p, "restoring VF%u config:\n", vfid);
xe_guc_klv_print(buf, size / sizeof(u32), &p);
@@ -2552,10 +2552,10 @@ int xe_gt_sriov_pf_config_print_lmem(struct xe_gt *gt, struct drm_printer *p)
if (!config->lmem_obj)
continue;
string_get_size(config->lmem_obj->size, 1, STRING_UNITS_2,
string_get_size(xe_bo_size(config->lmem_obj), 1, STRING_UNITS_2,
buf, sizeof(buf));
drm_printf(p, "VF%u:\t%zu\t(%s)\n",
n, config->lmem_obj->size, buf);
n, xe_bo_size(config->lmem_obj), buf);
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
+1 -1
View File
@@ -266,7 +266,7 @@ static void pf_prepare_runtime_info(struct xe_gt *gt)
read_many(gt, size, regs, values);
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
struct drm_printer p = xe_gt_info_printer(gt);
struct drm_printer p = xe_gt_dbg_printer(gt);
xe_gt_sriov_pf_service_print_runtime(gt, &p);
}
-1
View File
@@ -966,7 +966,6 @@ u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg)
struct vf_runtime_reg *rr;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
xe_gt_assert(gt, gt->sriov.vf.pf_version.major);
xe_gt_assert(gt, !reg.vf);
if (reg.addr == GMD_ID.addr) {
+2
View File
@@ -377,6 +377,8 @@ struct xe_gt {
u16 group_target;
/** @steering.instance_target: instance to steer accesses to */
u16 instance_target;
/** @steering.initialized: Whether this steering range is initialized */
bool initialized;
} steering[NUM_STEERING_TYPES];
/**
+129 -18
View File
@@ -29,6 +29,7 @@
#include "xe_guc_db_mgr.h"
#include "xe_guc_engine_activity.h"
#include "xe_guc_hwconfig.h"
#include "xe_guc_klv_helpers.h"
#include "xe_guc_log.h"
#include "xe_guc_pc.h"
#include "xe_guc_relay.h"
@@ -59,7 +60,7 @@ static u32 guc_bo_ggtt_addr(struct xe_guc *guc,
/* GuC addresses above GUC_GGTT_TOP don't map through the GTT */
xe_assert(xe, addr >= xe_wopcm_size(guc_to_xe(guc)));
xe_assert(xe, addr < GUC_GGTT_TOP);
xe_assert(xe, bo->size <= GUC_GGTT_TOP - addr);
xe_assert(xe, xe_bo_size(bo) <= GUC_GGTT_TOP - addr);
return addr;
}
@@ -420,7 +421,7 @@ static int guc_g2g_register(struct xe_guc *near_guc, struct xe_gt *far_gt, u32 t
buf = base + G2G_DESC_AREA_SIZE + slot * G2G_BUFFER_SIZE;
xe_assert(xe, (desc - base + G2G_DESC_SIZE) <= G2G_DESC_AREA_SIZE);
xe_assert(xe, (buf - base + G2G_BUFFER_SIZE) <= g2g_bo->size);
xe_assert(xe, (buf - base + G2G_BUFFER_SIZE) <= xe_bo_size(g2g_bo));
return guc_action_register_g2g_buffer(near_guc, type, far_tile, far_dev,
desc, buf, G2G_BUFFER_SIZE);
@@ -570,6 +571,86 @@ err_deregister:
return err;
}
static int __guc_opt_in_features_enable(struct xe_guc *guc, u64 addr, u32 num_dwords)
{
u32 action[] = {
XE_GUC_ACTION_OPT_IN_FEATURE_KLV,
lower_32_bits(addr),
upper_32_bits(addr),
num_dwords
};
return xe_guc_ct_send_block(&guc->ct, action, ARRAY_SIZE(action));
}
static bool supports_dynamic_ics(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
/* Dynamic ICS is available for PVC and Xe2 and newer platforms. */
if (xe->info.platform != XE_PVC && GRAPHICS_VER(xe) < 20)
return false;
/*
* The feature is currently not compatible with multi-lrc, so the GuC
* does not support it at all on the media engines (which are the main
* users of mlrc). On the primary GT side, to avoid it being used in
* conjunction with mlrc, we only enable it if we are in single CCS
* mode.
*/
if (xe_gt_is_media_type(gt) || gt->ccs_mode > 1)
return false;
/*
* Dynamic ICS requires GuC v70.40.1, which maps to compatibility
* version v1.18.4.
*/
return GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 18, 4);
}
#define OPT_IN_MAX_DWORDS 16
int xe_guc_opt_in_features_enable(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
CLASS(xe_guc_buf, buf)(&guc->buf, OPT_IN_MAX_DWORDS);
u32 count = 0;
u32 *klvs;
int ret;
if (!xe_guc_buf_is_valid(buf))
return -ENOBUFS;
klvs = xe_guc_buf_cpu_ptr(buf);
/*
* The extra CAT error type opt-in was added in GuC v70.17.0, which maps
* to compatibility version v1.7.0.
* Note that the GuC allows enabling this KLV even on platforms that do
* not support the extra type; in such case the returned type variable
* will be set to a known invalid value which we can check against.
*/
if (GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 7, 0))
klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_EXT_CAT_ERR_TYPE);
if (supports_dynamic_ics(guc))
klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH);
if (count) {
xe_assert(xe, count <= OPT_IN_MAX_DWORDS);
ret = __guc_opt_in_features_enable(guc, xe_guc_buf_flush(buf), count);
if (ret < 0) {
xe_gt_err(guc_to_gt(guc),
"failed to enable GuC opt-in features: %pe\n",
ERR_PTR(ret));
return ret;
}
}
return 0;
}
static void guc_fini_hw(void *arg)
{
struct xe_guc *guc = arg;
@@ -577,7 +658,7 @@ static void guc_fini_hw(void *arg)
unsigned int fw_ref;
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
xe_uc_fini_hw(&guc_to_gt(guc)->uc);
xe_uc_sanitize_reset(&guc_to_gt(guc)->uc);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
guc_g2g_fini(guc);
@@ -627,23 +708,51 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc)
return 0;
}
static int vf_guc_init(struct xe_guc *guc)
static int vf_guc_init_noalloc(struct xe_guc *guc)
{
struct xe_gt *gt = guc_to_gt(guc);
int err;
xe_guc_comm_init_early(guc);
err = xe_guc_ct_init(&guc->ct);
err = xe_gt_sriov_vf_bootstrap(gt);
if (err)
return err;
err = xe_guc_relay_init(&guc->relay);
err = xe_gt_sriov_vf_query_config(gt);
if (err)
return err;
return 0;
}
int xe_guc_init_noalloc(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
int ret;
xe_guc_comm_init_early(guc);
ret = xe_guc_ct_init_noalloc(&guc->ct);
if (ret)
goto out;
ret = xe_guc_relay_init(&guc->relay);
if (ret)
goto out;
if (IS_SRIOV_VF(xe)) {
ret = vf_guc_init_noalloc(guc);
if (ret)
goto out;
}
return 0;
out:
xe_gt_err(gt, "GuC init failed with %pe\n", ERR_PTR(ret));
return ret;
}
int xe_guc_init(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
@@ -653,13 +762,13 @@ int xe_guc_init(struct xe_guc *guc)
guc->fw.type = XE_UC_FW_TYPE_GUC;
ret = xe_uc_fw_init(&guc->fw);
if (ret)
goto out;
return ret;
if (!xe_uc_fw_is_enabled(&guc->fw))
return 0;
if (IS_SRIOV_VF(xe)) {
ret = vf_guc_init(guc);
ret = xe_guc_ct_init(&guc->ct);
if (ret)
goto out;
return 0;
@@ -681,10 +790,6 @@ int xe_guc_init(struct xe_guc *guc)
if (ret)
goto out;
ret = xe_guc_relay_init(&guc->relay);
if (ret)
goto out;
xe_uc_fw_change_status(&guc->fw, XE_UC_FIRMWARE_LOADABLE);
ret = devm_add_action_or_reset(xe->drm.dev, guc_fini_hw, guc);
@@ -693,8 +798,6 @@ int xe_guc_init(struct xe_guc *guc)
guc_init_params(guc);
xe_guc_comm_init_early(guc);
return 0;
out:
@@ -767,6 +870,10 @@ int xe_guc_post_load_init(struct xe_guc *guc)
xe_guc_ads_populate_post_load(&guc->ads);
ret = xe_guc_opt_in_features_enable(guc);
if (ret)
return ret;
if (xe_guc_g2g_wanted(guc_to_xe(guc))) {
ret = guc_g2g_start(guc);
if (ret)
@@ -1112,13 +1219,17 @@ static int vf_guc_min_load_for_hwconfig(struct xe_guc *guc)
ret = xe_gt_sriov_vf_connect(gt);
if (ret)
return ret;
goto err_out;
ret = xe_gt_sriov_vf_query_runtime(gt);
if (ret)
return ret;
goto err_out;
return 0;
err_out:
xe_guc_sanitize(guc);
return ret;
}
/**
+2
View File
@@ -26,6 +26,7 @@
struct drm_printer;
void xe_guc_comm_init_early(struct xe_guc *guc);
int xe_guc_init_noalloc(struct xe_guc *guc);
int xe_guc_init(struct xe_guc *guc);
int xe_guc_init_post_hwconfig(struct xe_guc *guc);
int xe_guc_post_load_init(struct xe_guc *guc);
@@ -33,6 +34,7 @@ int xe_guc_reset(struct xe_guc *guc);
int xe_guc_upload(struct xe_guc *guc);
int xe_guc_min_load_for_hwconfig(struct xe_guc *guc);
int xe_guc_enable_communication(struct xe_guc *guc);
int xe_guc_opt_in_features_enable(struct xe_guc *guc);
int xe_guc_suspend(struct xe_guc *guc);
void xe_guc_notify(struct xe_guc *guc);
int xe_guc_auth_huc(struct xe_guc *guc, u32 rsa_addr);
+11 -20
View File
@@ -890,7 +890,7 @@ void xe_guc_ads_populate_minimal(struct xe_guc_ads *ads)
xe_gt_assert(gt, ads->bo);
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, ads->bo->size);
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, xe_bo_size(ads->bo));
guc_policies_init(ads);
guc_golden_lrc_init(ads);
guc_mapping_table_init_invalid(gt, &info_map);
@@ -914,7 +914,7 @@ void xe_guc_ads_populate(struct xe_guc_ads *ads)
xe_gt_assert(gt, ads->bo);
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, ads->bo->size);
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, xe_bo_size(ads->bo));
guc_policies_init(ads);
fill_engine_enable_masks(gt, &info_map);
guc_mmio_reg_state_init(ads);
@@ -995,16 +995,6 @@ static int guc_ads_action_update_policies(struct xe_guc_ads *ads, u32 policy_off
return xe_guc_ct_send(ct, action, ARRAY_SIZE(action), 0, 0);
}
static int guc_ads_update_policies(struct xe_guc_ads *ads, const struct guc_policies *policies)
{
CLASS(xe_guc_buf_from_data, buf)(&ads_to_guc(ads)->buf, policies, sizeof(*policies));
if (!xe_guc_buf_is_valid(buf))
return -ENOBUFS;
return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf));
}
/**
* xe_guc_ads_scheduler_policy_toggle_reset - Toggle reset policy
* @ads: Additional data structures object
@@ -1015,13 +1005,16 @@ static int guc_ads_update_policies(struct xe_guc_ads *ads, const struct guc_poli
*/
int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
{
struct xe_device *xe = ads_to_xe(ads);
struct guc_policies *policies;
int ret;
struct xe_guc *guc = ads_to_guc(ads);
struct xe_device *xe = ads_to_xe(ads);
CLASS(xe_guc_buf, buf)(&guc->buf, sizeof(*policies));
policies = kmalloc(sizeof(*policies), GFP_KERNEL);
if (!policies)
return -ENOMEM;
if (!xe_guc_buf_is_valid(buf))
return -ENOBUFS;
policies = xe_guc_buf_cpu_ptr(buf);
memset(policies, 0, sizeof(*policies));
policies->dpc_promote_time = ads_blob_read(ads, policies.dpc_promote_time);
policies->max_num_work_items = ads_blob_read(ads, policies.max_num_work_items);
@@ -1031,7 +1024,5 @@ int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
else
policies->global_flags &= ~GLOBAL_POLICY_DISABLE_ENGINE_RESET;
ret = guc_ads_update_policies(ads, policies);
kfree(policies);
return ret;
return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf));
}
+60 -21
View File
@@ -35,6 +35,11 @@
#include "xe_pm.h"
#include "xe_trace_guc.h"
static void receive_g2h(struct xe_guc_ct *ct);
static void g2h_worker_func(struct work_struct *w);
static void safe_mode_worker_func(struct work_struct *w);
static void ct_exit_safe_mode(struct xe_guc_ct *ct);
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
enum {
/* Internal states, not error conditions */
@@ -80,6 +85,7 @@ struct g2h_fence {
u16 error;
u16 hint;
u16 reason;
bool cancel;
bool retry;
bool fail;
bool done;
@@ -98,6 +104,13 @@ static void g2h_fence_init(struct g2h_fence *g2h_fence, u32 *response_buffer)
g2h_fence->seqno = ~0x0;
}
static void g2h_fence_cancel(struct g2h_fence *g2h_fence)
{
g2h_fence->cancel = true;
g2h_fence->fail = true;
g2h_fence->done = true;
}
static bool g2h_fence_needs_alloc(struct g2h_fence *g2h_fence)
{
return g2h_fence->seqno == ~0x0;
@@ -189,14 +202,11 @@ static void guc_ct_fini(struct drm_device *drm, void *arg)
{
struct xe_guc_ct *ct = arg;
ct_exit_safe_mode(ct);
destroy_workqueue(ct->g2h_wq);
xa_destroy(&ct->fence_lookup);
}
static void receive_g2h(struct xe_guc_ct *ct);
static void g2h_worker_func(struct work_struct *w);
static void safe_mode_worker_func(struct work_struct *w);
static void primelockdep(struct xe_guc_ct *ct)
{
if (!IS_ENABLED(CONFIG_LOCKDEP))
@@ -207,12 +217,10 @@ static void primelockdep(struct xe_guc_ct *ct)
fs_reclaim_release(GFP_KERNEL);
}
int xe_guc_ct_init(struct xe_guc_ct *ct)
int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct)
{
struct xe_device *xe = ct_to_xe(ct);
struct xe_gt *gt = ct_to_gt(ct);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_bo *bo;
int err;
xe_gt_assert(gt, !(guc_ct_size() % PAGE_SIZE));
@@ -238,6 +246,23 @@ int xe_guc_ct_init(struct xe_guc_ct *ct)
primelockdep(ct);
err = drmm_add_action_or_reset(&xe->drm, guc_ct_fini, ct);
if (err)
return err;
xe_gt_assert(gt, ct->state == XE_GUC_CT_STATE_NOT_INITIALIZED);
ct->state = XE_GUC_CT_STATE_DISABLED;
return 0;
}
ALLOW_ERROR_INJECTION(xe_guc_ct_init_noalloc, ERRNO); /* See xe_pci_probe() */
int xe_guc_ct_init(struct xe_guc_ct *ct)
{
struct xe_device *xe = ct_to_xe(ct);
struct xe_gt *gt = ct_to_gt(ct);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_bo *bo;
bo = xe_managed_bo_create_pin_map(xe, tile, guc_ct_size(),
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
@@ -247,13 +272,6 @@ int xe_guc_ct_init(struct xe_guc_ct *ct)
return PTR_ERR(bo);
ct->bo = bo;
err = drmm_add_action_or_reset(&xe->drm, guc_ct_fini, ct);
if (err)
return err;
xe_gt_assert(gt, ct->state == XE_GUC_CT_STATE_NOT_INITIALIZED);
ct->state = XE_GUC_CT_STATE_DISABLED;
return 0;
}
ALLOW_ERROR_INJECTION(xe_guc_ct_init, ERRNO); /* See xe_pci_probe() */
@@ -374,9 +392,13 @@ static int guc_ct_control_toggle(struct xe_guc_ct *ct, bool enable)
return ret > 0 ? -EPROTO : ret;
}
static void xe_guc_ct_set_state(struct xe_guc_ct *ct,
static void guc_ct_change_state(struct xe_guc_ct *ct,
enum xe_guc_ct_state state)
{
struct xe_gt *gt = ct_to_gt(ct);
struct g2h_fence *g2h_fence;
unsigned long idx;
mutex_lock(&ct->lock); /* Serialise dequeue_one_g2h() */
spin_lock_irq(&ct->fast_lock); /* Serialise CT fast-path */
@@ -388,8 +410,20 @@ static void xe_guc_ct_set_state(struct xe_guc_ct *ct,
ct->g2h_outstanding = 0;
ct->state = state;
xe_gt_dbg(gt, "GuC CT communication channel %s\n",
state == XE_GUC_CT_STATE_STOPPED ? "stopped" :
str_enabled_disabled(state == XE_GUC_CT_STATE_ENABLED));
spin_unlock_irq(&ct->fast_lock);
/* cancel all in-flight send-recv requests */
xa_for_each(&ct->fence_lookup, idx, g2h_fence)
g2h_fence_cancel(g2h_fence);
/* make sure guc_ct_send_recv() will see g2h_fence changes */
smp_mb();
wake_up_all(&ct->g2h_fence_wq);
/*
* Lockdep doesn't like this under the fast lock and he destroy only
* needs to be serialized with the send path which ct lock provides.
@@ -443,7 +477,7 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
xe_gt_assert(gt, !xe_guc_ct_enabled(ct));
xe_map_memset(xe, &ct->bo->vmap, 0, 0, ct->bo->size);
xe_map_memset(xe, &ct->bo->vmap, 0, 0, xe_bo_size(ct->bo));
guc_ct_ctb_h2g_init(xe, &ct->ctbs.h2g, &ct->bo->vmap);
guc_ct_ctb_g2h_init(xe, &ct->ctbs.g2h, &ct->bo->vmap);
@@ -459,11 +493,10 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
if (err)
goto err_out;
xe_guc_ct_set_state(ct, XE_GUC_CT_STATE_ENABLED);
guc_ct_change_state(ct, XE_GUC_CT_STATE_ENABLED);
smp_mb();
wake_up_all(&ct->wq);
xe_gt_dbg(gt, "GuC CT communication channel enabled\n");
if (ct_needs_safe_mode(ct))
ct_enter_safe_mode(ct);
@@ -504,7 +537,7 @@ static void stop_g2h_handler(struct xe_guc_ct *ct)
*/
void xe_guc_ct_disable(struct xe_guc_ct *ct)
{
xe_guc_ct_set_state(ct, XE_GUC_CT_STATE_DISABLED);
guc_ct_change_state(ct, XE_GUC_CT_STATE_DISABLED);
ct_exit_safe_mode(ct);
stop_g2h_handler(ct);
}
@@ -520,7 +553,7 @@ void xe_guc_ct_stop(struct xe_guc_ct *ct)
if (!xe_guc_ct_initialized(ct))
return;
xe_guc_ct_set_state(ct, XE_GUC_CT_STATE_STOPPED);
guc_ct_change_state(ct, XE_GUC_CT_STATE_STOPPED);
stop_g2h_handler(ct);
}
@@ -1083,6 +1116,11 @@ retry_same_fence:
goto retry;
}
if (g2h_fence.fail) {
if (g2h_fence.cancel) {
xe_gt_dbg(gt, "H2G request %#x canceled!\n", action[0]);
ret = -ECANCELED;
goto unlock;
}
xe_gt_err(gt, "H2G request %#x failed: error %#x hint %#x\n",
action[0], g2h_fence.error, g2h_fence.hint);
ret = -EIO;
@@ -1091,6 +1129,7 @@ retry_same_fence:
if (ret > 0)
ret = response_buffer ? g2h_fence.response_len : g2h_fence.response_data;
unlock:
mutex_unlock(&ct->lock);
return ret;
@@ -1897,7 +1936,7 @@ static struct xe_guc_ct_snapshot *guc_ct_snapshot_alloc(struct xe_guc_ct *ct, bo
return NULL;
if (ct->bo && want_ctb) {
snapshot->ctb_size = ct->bo->size;
snapshot->ctb_size = xe_bo_size(ct->bo);
snapshot->ctb = kmalloc(snapshot->ctb_size, atomic ? GFP_ATOMIC : GFP_KERNEL);
}
+1
View File
@@ -11,6 +11,7 @@
struct drm_printer;
struct xe_device;
int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct);
int xe_guc_ct_init(struct xe_guc_ct *ct);
int xe_guc_ct_enable(struct xe_guc_ct *ct);
void xe_guc_ct_disable(struct xe_guc_ct *ct);
+1 -1
View File
@@ -79,7 +79,7 @@ static struct xe_guc_log_snapshot *xe_guc_log_snapshot_alloc(struct xe_guc_log *
* Also, can't use vmalloc as might be called from atomic context. So need
* to break the buffer up into smaller chunks that can be allocated.
*/
snapshot->size = log->bo->size;
snapshot->size = xe_bo_size(log->bo);
snapshot->num_chunks = DIV_ROUND_UP(snapshot->size, GUC_LOG_CHUNK_SIZE);
snapshot->copy = kcalloc(snapshot->num_chunks, sizeof(*snapshot->copy),
+212 -71
View File
@@ -5,8 +5,11 @@
#include "xe_guc_pc.h"
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/wait_bit.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
@@ -52,9 +55,11 @@
#define LNL_MERT_FREQ_CAP 800
#define BMG_MERT_FREQ_CAP 2133
#define BMG_MIN_FREQ 1200
#define BMG_MERT_FLUSH_FREQ_CAP 2600
#define SLPC_RESET_TIMEOUT_MS 5 /* roughly 5ms, but no need for precision */
#define SLPC_RESET_EXTENDED_TIMEOUT_MS 1000 /* To be used only at pc_start */
#define SLPC_ACT_FREQ_TIMEOUT_MS 100
/**
* DOC: GuC Power Conservation (PC)
@@ -142,6 +147,36 @@ static int wait_for_pc_state(struct xe_guc_pc *pc,
return -ETIMEDOUT;
}
static int wait_for_flush_complete(struct xe_guc_pc *pc)
{
const unsigned long timeout = msecs_to_jiffies(30);
if (!wait_var_event_timeout(&pc->flush_freq_limit,
!atomic_read(&pc->flush_freq_limit),
timeout))
return -ETIMEDOUT;
return 0;
}
static int wait_for_act_freq_limit(struct xe_guc_pc *pc, u32 freq)
{
int timeout_us = SLPC_ACT_FREQ_TIMEOUT_MS * USEC_PER_MSEC;
int slept, wait = 10;
for (slept = 0; slept < timeout_us;) {
if (xe_guc_pc_get_act_freq(pc) <= freq)
return 0;
usleep_range(wait, wait << 1);
slept += wait;
wait <<= 1;
if (slept + wait > timeout_us)
wait = timeout_us - slept;
}
return -ETIMEDOUT;
}
static int pc_action_reset(struct xe_guc_pc *pc)
{
struct xe_guc_ct *ct = pc_to_ct(pc);
@@ -554,6 +589,25 @@ u32 xe_guc_pc_get_rpn_freq(struct xe_guc_pc *pc)
return pc->rpn_freq;
}
static int xe_guc_pc_get_min_freq_locked(struct xe_guc_pc *pc, u32 *freq)
{
int ret;
lockdep_assert_held(&pc->freq_lock);
/* Might be in the middle of a gt reset */
if (!pc->freq_ready)
return -EAGAIN;
ret = pc_action_query_task_state(pc);
if (ret)
return ret;
*freq = pc_get_min_freq(pc);
return 0;
}
/**
* xe_guc_pc_get_min_freq - Get the min operational frequency
* @pc: The GuC PC
@@ -563,27 +617,29 @@ u32 xe_guc_pc_get_rpn_freq(struct xe_guc_pc *pc)
* -EAGAIN if GuC PC not ready (likely in middle of a reset).
*/
int xe_guc_pc_get_min_freq(struct xe_guc_pc *pc, u32 *freq)
{
guard(mutex)(&pc->freq_lock);
return xe_guc_pc_get_min_freq_locked(pc, freq);
}
static int xe_guc_pc_set_min_freq_locked(struct xe_guc_pc *pc, u32 freq)
{
int ret;
xe_device_assert_mem_access(pc_to_xe(pc));
lockdep_assert_held(&pc->freq_lock);
mutex_lock(&pc->freq_lock);
if (!pc->freq_ready) {
/* Might be in the middle of a gt reset */
ret = -EAGAIN;
goto out;
}
/* Might be in the middle of a gt reset */
if (!pc->freq_ready)
return -EAGAIN;
ret = pc_action_query_task_state(pc);
ret = pc_set_min_freq(pc, freq);
if (ret)
goto out;
return ret;
*freq = pc_get_min_freq(pc);
pc->user_requested_min = freq;
out:
mutex_unlock(&pc->freq_lock);
return ret;
return 0;
}
/**
@@ -596,25 +652,29 @@ out:
* -EINVAL if value out of bounds.
*/
int xe_guc_pc_set_min_freq(struct xe_guc_pc *pc, u32 freq)
{
guard(mutex)(&pc->freq_lock);
return xe_guc_pc_set_min_freq_locked(pc, freq);
}
static int xe_guc_pc_get_max_freq_locked(struct xe_guc_pc *pc, u32 *freq)
{
int ret;
mutex_lock(&pc->freq_lock);
if (!pc->freq_ready) {
/* Might be in the middle of a gt reset */
ret = -EAGAIN;
goto out;
}
lockdep_assert_held(&pc->freq_lock);
ret = pc_set_min_freq(pc, freq);
/* Might be in the middle of a gt reset */
if (!pc->freq_ready)
return -EAGAIN;
ret = pc_action_query_task_state(pc);
if (ret)
goto out;
return ret;
pc->user_requested_min = freq;
*freq = pc_get_max_freq(pc);
out:
mutex_unlock(&pc->freq_lock);
return ret;
return 0;
}
/**
@@ -626,25 +686,29 @@ out:
* -EAGAIN if GuC PC not ready (likely in middle of a reset).
*/
int xe_guc_pc_get_max_freq(struct xe_guc_pc *pc, u32 *freq)
{
guard(mutex)(&pc->freq_lock);
return xe_guc_pc_get_max_freq_locked(pc, freq);
}
static int xe_guc_pc_set_max_freq_locked(struct xe_guc_pc *pc, u32 freq)
{
int ret;
mutex_lock(&pc->freq_lock);
if (!pc->freq_ready) {
/* Might be in the middle of a gt reset */
ret = -EAGAIN;
goto out;
}
lockdep_assert_held(&pc->freq_lock);
ret = pc_action_query_task_state(pc);
/* Might be in the middle of a gt reset */
if (!pc->freq_ready)
return -EAGAIN;
ret = pc_set_max_freq(pc, freq);
if (ret)
goto out;
return ret;
*freq = pc_get_max_freq(pc);
pc->user_requested_max = freq;
out:
mutex_unlock(&pc->freq_lock);
return ret;
return 0;
}
/**
@@ -658,24 +722,14 @@ out:
*/
int xe_guc_pc_set_max_freq(struct xe_guc_pc *pc, u32 freq)
{
int ret;
mutex_lock(&pc->freq_lock);
if (!pc->freq_ready) {
/* Might be in the middle of a gt reset */
ret = -EAGAIN;
goto out;
if (XE_WA(pc_to_gt(pc), 22019338487)) {
if (wait_for_flush_complete(pc) != 0)
return -EAGAIN;
}
ret = pc_set_max_freq(pc, freq);
if (ret)
goto out;
guard(mutex)(&pc->freq_lock);
pc->user_requested_max = freq;
out:
mutex_unlock(&pc->freq_lock);
return ret;
return xe_guc_pc_set_max_freq_locked(pc, freq);
}
/**
@@ -873,29 +927,116 @@ static int pc_adjust_requested_freq(struct xe_guc_pc *pc)
return ret;
}
static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
static bool needs_flush_freq_limit(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
return XE_WA(gt, 22019338487) &&
pc->rp0_freq > BMG_MERT_FLUSH_FREQ_CAP;
}
/**
* xe_guc_pc_apply_flush_freq_limit() - Limit max GT freq during L2 flush
* @pc: the xe_guc_pc object
*
* As per the WA, reduce max GT frequency during L2 cache flush
*/
void xe_guc_pc_apply_flush_freq_limit(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
u32 max_freq;
int ret;
if (!needs_flush_freq_limit(pc))
return;
guard(mutex)(&pc->freq_lock);
ret = xe_guc_pc_get_max_freq_locked(pc, &max_freq);
if (!ret && max_freq > BMG_MERT_FLUSH_FREQ_CAP) {
ret = pc_set_max_freq(pc, BMG_MERT_FLUSH_FREQ_CAP);
if (ret) {
xe_gt_err_once(gt, "Failed to cap max freq on flush to %u, %pe\n",
BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret));
return;
}
atomic_set(&pc->flush_freq_limit, 1);
/*
* If user has previously changed max freq, stash that value to
* restore later, otherwise use the current max. New user
* requests wait on flush.
*/
if (pc->user_requested_max != 0)
pc->stashed_max_freq = pc->user_requested_max;
else
pc->stashed_max_freq = max_freq;
}
/*
* Wait for actual freq to go below the flush cap: even if the previous
* max was below cap, the current one might still be above it
*/
ret = wait_for_act_freq_limit(pc, BMG_MERT_FLUSH_FREQ_CAP);
if (ret)
xe_gt_err_once(gt, "Actual freq did not reduce to %u, %pe\n",
BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret));
}
/**
* xe_guc_pc_remove_flush_freq_limit() - Remove max GT freq limit after L2 flush completes.
* @pc: the xe_guc_pc object
*
* Retrieve the previous GT max frequency value.
*/
void xe_guc_pc_remove_flush_freq_limit(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
int ret = 0;
if (XE_WA(pc_to_gt(pc), 22019338487)) {
/*
* Get updated min/max and stash them.
*/
ret = xe_guc_pc_get_min_freq(pc, &pc->stashed_min_freq);
if (!ret)
ret = xe_guc_pc_get_max_freq(pc, &pc->stashed_max_freq);
if (ret)
return ret;
if (!needs_flush_freq_limit(pc))
return;
/*
* Ensure min and max are bound by MERT_FREQ_CAP until driver loads.
*/
mutex_lock(&pc->freq_lock);
ret = pc_set_min_freq(pc, min(pc->rpe_freq, pc_max_freq_cap(pc)));
if (!ret)
ret = pc_set_max_freq(pc, min(pc->rp0_freq, pc_max_freq_cap(pc)));
mutex_unlock(&pc->freq_lock);
}
if (!atomic_read(&pc->flush_freq_limit))
return;
mutex_lock(&pc->freq_lock);
ret = pc_set_max_freq(&gt->uc.guc.pc, pc->stashed_max_freq);
if (ret)
xe_gt_err_once(gt, "Failed to restore max freq %u:%d",
pc->stashed_max_freq, ret);
atomic_set(&pc->flush_freq_limit, 0);
mutex_unlock(&pc->freq_lock);
wake_up_var(&pc->flush_freq_limit);
}
static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
{
int ret;
if (!XE_WA(pc_to_gt(pc), 22019338487))
return 0;
guard(mutex)(&pc->freq_lock);
/*
* Get updated min/max and stash them.
*/
ret = xe_guc_pc_get_min_freq_locked(pc, &pc->stashed_min_freq);
if (!ret)
ret = xe_guc_pc_get_max_freq_locked(pc, &pc->stashed_max_freq);
if (ret)
return ret;
/*
* Ensure min and max are bound by MERT_FREQ_CAP until driver loads.
*/
ret = pc_set_min_freq(pc, min(pc->rpe_freq, pc_max_freq_cap(pc)));
if (!ret)
ret = pc_set_max_freq(pc, min(pc->rp0_freq, pc_max_freq_cap(pc)));
return ret;
}
+2
View File
@@ -38,5 +38,7 @@ u64 xe_guc_pc_mc6_residency(struct xe_guc_pc *pc);
void xe_guc_pc_init_early(struct xe_guc_pc *pc);
int xe_guc_pc_restore_stashed_freq(struct xe_guc_pc *pc);
void xe_guc_pc_raise_unslice(struct xe_guc_pc *pc);
void xe_guc_pc_apply_flush_freq_limit(struct xe_guc_pc *pc);
void xe_guc_pc_remove_flush_freq_limit(struct xe_guc_pc *pc);
#endif /* _XE_GUC_PC_H_ */
+2
View File
@@ -15,6 +15,8 @@
struct xe_guc_pc {
/** @bo: GGTT buffer object that is shared with GuC PC */
struct xe_bo *bo;
/** @flush_freq_limit: 1 when max freq changes are limited by driver */
atomic_t flush_freq_limit;
/** @rp0_freq: HW RP0 frequency - The Maximum one */
u32 rp0_freq;
/** @rpa_freq: HW RPa frequency - The Achievable one */
+24 -7
View File
@@ -908,12 +908,13 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
struct xe_exec_queue *q = ge->q;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_gpu_scheduler *sched = &ge->sched;
bool wedged;
bool wedged = false;
xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_lr(q));
trace_xe_exec_queue_lr_cleanup(q);
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
if (!exec_queue_killed(q))
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
/* Kill the run_job / process_msg entry points */
xe_sched_submission_stop(sched);
@@ -1084,7 +1085,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
int err = -ETIME;
pid_t pid = -1;
int i = 0;
bool wedged, skip_timeout_check;
bool wedged = false, skip_timeout_check;
/*
* TDR has fired before free job worker. Common if exec queue
@@ -1130,7 +1131,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
* doesn't work for SRIOV. For now assuming timeouts in wedged mode are
* genuine timeouts.
*/
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
if (!exec_queue_killed(q))
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
/* Engine state now stable, disable scheduling to check timestamp */
if (!wedged && exec_queue_registered(q)) {
@@ -2090,12 +2092,16 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
struct xe_gt *gt = guc_to_gt(guc);
struct xe_exec_queue *q;
u32 guc_id;
u32 type = XE_GUC_CAT_ERR_TYPE_INVALID;
if (unlikely(len < 1))
if (unlikely(!len || len > 2))
return -EPROTO;
guc_id = msg[0];
if (len == 2)
type = msg[1];
if (guc_id == GUC_ID_UNKNOWN) {
/*
* GuC uses GUC_ID_UNKNOWN if it can not map the CAT fault to any PF/VF
@@ -2109,8 +2115,19 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
if (unlikely(!q))
return -EPROTO;
xe_gt_dbg(gt, "Engine memory cat error: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
/*
* The type is HW-defined and changes based on platform, so we don't
* decode it in the kernel and only check if it is valid.
* See bspec 54047 and 72187 for details.
*/
if (type != XE_GUC_CAT_ERR_TYPE_INVALID)
xe_gt_dbg(gt,
"Engine memory CAT error [%u]: class=%s, logical_mask: 0x%x, guc_id=%d",
type, xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
else
xe_gt_dbg(gt,
"Engine memory CAT error: class=%s, logical_mask: 0x%x, guc_id=%d",
xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
trace_xe_exec_queue_memory_cat_error(q);
+1 -4
View File
@@ -11,15 +11,12 @@
#include "xe_device_types.h"
#include "xe_drv.h"
#include "xe_heci_gsc.h"
#include "regs/xe_gsc_regs.h"
#include "xe_platform_types.h"
#include "xe_survivability_mode.h"
#define GSC_BAR_LENGTH 0x00000FFC
#define DG1_GSC_HECI2_BASE 0x259000
#define PVC_GSC_HECI2_BASE 0x285000
#define DG2_GSC_HECI2_BASE 0x374000
static void heci_gsc_irq_mask(struct irq_data *d)
{
/* generic irq handling */
+1 -1
View File
@@ -171,7 +171,7 @@ static int huc_auth_via_gsccs(struct xe_huc *huc)
sizeof(struct pxp43_new_huc_auth_in));
wr_offset = huc_emit_pxp_auth_msg(xe, &pkt->vmap, wr_offset,
xe_bo_ggtt_addr(huc->fw.bo),
huc->fw.bo->size);
xe_bo_size(huc->fw.bo));
do {
err = xe_gsc_pkt_submit_kernel(&gt->uc.gsc, ggtt_offset, wr_offset,
ggtt_offset + PXP43_HUC_AUTH_INOUT_SIZE,
+2 -1
View File
@@ -1059,12 +1059,13 @@ struct xe_hw_engine *
xe_hw_engine_lookup(struct xe_device *xe,
struct drm_xe_engine_class_instance eci)
{
struct xe_gt *gt = xe_device_get_gt(xe, eci.gt_id);
unsigned int idx;
if (eci.engine_class >= ARRAY_SIZE(user_to_xe_engine_class))
return NULL;
if (eci.gt_id >= xe->info.gt_count)
if (!gt)
return NULL;
idx = array_index_nospec(eci.engine_class,
+5 -14
View File
@@ -12,15 +12,6 @@
#include "xe_hw_engine_group.h"
#include "xe_vm.h"
static void
hw_engine_group_free(struct drm_device *drm, void *arg)
{
struct xe_hw_engine_group *group = arg;
destroy_workqueue(group->resume_wq);
kfree(group);
}
static void
hw_engine_group_resume_lr_jobs_func(struct work_struct *w)
{
@@ -53,7 +44,7 @@ hw_engine_group_alloc(struct xe_device *xe)
struct xe_hw_engine_group *group;
int err;
group = kzalloc(sizeof(*group), GFP_KERNEL);
group = drmm_kzalloc(&xe->drm, sizeof(*group), GFP_KERNEL);
if (!group)
return ERR_PTR(-ENOMEM);
@@ -61,14 +52,14 @@ hw_engine_group_alloc(struct xe_device *xe)
if (!group->resume_wq)
return ERR_PTR(-ENOMEM);
err = drmm_add_action_or_reset(&xe->drm, __drmm_workqueue_release, group->resume_wq);
if (err)
return ERR_PTR(err);
init_rwsem(&group->mode_sem);
INIT_WORK(&group->resume_work, hw_engine_group_resume_lr_jobs_func);
INIT_LIST_HEAD(&group->exec_queue_list);
err = drmm_add_action_or_reset(&xe->drm, hw_engine_group_free, group);
if (err)
return ERR_PTR(err);
return group;
}
+329
View File
@@ -0,0 +1,329 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* Intel Xe I2C attached Microcontroller Units (MCU)
*
* Copyright (C) 2025 Intel Corporation.
*/
#include <linux/array_size.h>
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/notifier.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/sprintf.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include "regs/xe_i2c_regs.h"
#include "regs/xe_irq_regs.h"
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_i2c.h"
#include "xe_mmio.h"
#include "xe_platform_types.h"
/**
* DOC: Xe I2C devices
*
* Register a platform device for the I2C host controller (Synpsys DesignWare
* I2C) if the registers of that controller are mapped to the MMIO, and also the
* I2C client device for the Add-In Management Controller (the MCU) attached to
* the host controller.
*
* See drivers/i2c/busses/i2c-designware-* for more information on the I2C host
* controller.
*/
static const char adapter_name[] = "i2c_designware";
static const struct property_entry xe_i2c_adapter_properties[] = {
PROPERTY_ENTRY_STRING("compatible", "intel,xe-i2c"),
PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_FAST_MODE_PLUS_FREQ),
{ }
};
static inline void xe_i2c_read_endpoint(struct xe_mmio *mmio, void *ep)
{
u32 *val = ep;
val[0] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_PREFIX);
val[1] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_POSTFIX);
}
static void xe_i2c_client_work(struct work_struct *work)
{
struct xe_i2c *i2c = container_of(work, struct xe_i2c, work);
struct i2c_board_info info = {
.type = "amc",
.flags = I2C_CLIENT_HOST_NOTIFY,
.addr = i2c->ep.addr[1],
};
i2c->client[0] = i2c_new_client_device(i2c->adapter, &info);
}
static int xe_i2c_notifier(struct notifier_block *nb, unsigned long action, void *data)
{
struct xe_i2c *i2c = container_of(nb, struct xe_i2c, bus_notifier);
struct i2c_adapter *adapter = i2c_verify_adapter(data);
struct device *dev = data;
if (action == BUS_NOTIFY_ADD_DEVICE &&
adapter && dev->parent == &i2c->pdev->dev) {
i2c->adapter = adapter;
schedule_work(&i2c->work);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static int xe_i2c_register_adapter(struct xe_i2c *i2c)
{
struct pci_dev *pci = to_pci_dev(i2c->drm_dev);
struct platform_device *pdev;
struct fwnode_handle *fwnode;
int ret;
fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
if (!fwnode)
return -ENOMEM;
/*
* Not using platform_device_register_full() here because we don't have
* a handle to the platform_device before it returns. xe_i2c_notifier()
* uses that handle, but it may be called before
* platform_device_register_full() is done.
*/
pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
if (!pdev) {
ret = -ENOMEM;
goto err_fwnode_remove;
}
if (i2c->adapter_irq) {
struct resource res;
res = DEFINE_RES_IRQ_NAMED(i2c->adapter_irq, "xe_i2c");
ret = platform_device_add_resources(pdev, &res, 1);
if (ret)
goto err_pdev_put;
}
pdev->dev.parent = i2c->drm_dev;
pdev->dev.fwnode = fwnode;
i2c->adapter_node = fwnode;
i2c->pdev = pdev;
ret = platform_device_add(pdev);
if (ret)
goto err_pdev_put;
return 0;
err_pdev_put:
platform_device_put(pdev);
err_fwnode_remove:
fwnode_remove_software_node(fwnode);
return ret;
}
static void xe_i2c_unregister_adapter(struct xe_i2c *i2c)
{
platform_device_unregister(i2c->pdev);
fwnode_remove_software_node(i2c->adapter_node);
}
/**
* xe_i2c_irq_handler: Handler for I2C interrupts
* @xe: xe device instance
* @master_ctl: interrupt register
*
* Forward interrupts generated by the I2C host adapter to the I2C host adapter
* driver.
*/
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl)
{
if (!xe->i2c || !xe->i2c->adapter_irq)
return;
if (master_ctl & I2C_IRQ)
generic_handle_irq_safe(xe->i2c->adapter_irq);
}
static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw_irq_num)
{
irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
return 0;
}
static const struct irq_domain_ops xe_i2c_irq_ops = {
.map = xe_i2c_irq_map,
};
static int xe_i2c_create_irq(struct xe_i2c *i2c)
{
struct irq_domain *domain;
if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ))
return 0;
domain = irq_domain_create_linear(dev_fwnode(i2c->drm_dev), 1, &xe_i2c_irq_ops, NULL);
if (!domain)
return -ENOMEM;
i2c->adapter_irq = irq_create_mapping(domain, 0);
i2c->irqdomain = domain;
return 0;
}
static void xe_i2c_remove_irq(struct xe_i2c *i2c)
{
if (!i2c->irqdomain)
return;
irq_dispose_mapping(i2c->adapter_irq);
irq_domain_remove(i2c->irqdomain);
}
static int xe_i2c_read(void *context, unsigned int reg, unsigned int *val)
{
struct xe_i2c *i2c = context;
*val = xe_mmio_read32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET));
return 0;
}
static int xe_i2c_write(void *context, unsigned int reg, unsigned int val)
{
struct xe_i2c *i2c = context;
xe_mmio_write32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET), val);
return 0;
}
static const struct regmap_config i2c_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_read = xe_i2c_read,
.reg_write = xe_i2c_write,
.fast_io = true,
};
void xe_i2c_pm_suspend(struct xe_device *xe)
{
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
return;
xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot);
drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR));
}
void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold)
{
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
return;
if (d3cold)
xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, 0, PCI_COMMAND_MEMORY);
xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0);
drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR));
}
static void xe_i2c_remove(void *data)
{
struct xe_i2c *i2c = data;
unsigned int i;
for (i = 0; i < XE_I2C_MAX_CLIENTS; i++)
i2c_unregister_device(i2c->client[i]);
bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
xe_i2c_unregister_adapter(i2c);
xe_i2c_remove_irq(i2c);
}
/**
* xe_i2c_probe: Probe the I2C host adapter and the I2C clients attached to it
* @xe: xe device instance
*
* Register all the I2C devices described in the I2C Endpoint data structure.
*
* Return: 0 on success, error code on failure
*/
int xe_i2c_probe(struct xe_device *xe)
{
struct device *drm_dev = xe->drm.dev;
struct xe_i2c_endpoint ep;
struct regmap *regmap;
struct xe_i2c *i2c;
int ret;
if (xe->info.platform != XE_BATTLEMAGE)
return 0;
xe_i2c_read_endpoint(xe_root_tile_mmio(xe), &ep);
if (ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
return 0;
i2c = devm_kzalloc(drm_dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
INIT_WORK(&i2c->work, xe_i2c_client_work);
i2c->mmio = xe_root_tile_mmio(xe);
i2c->drm_dev = drm_dev;
i2c->ep = ep;
xe->i2c = i2c;
/* PCI PM isn't aware of this device, bring it up and match it with SGUnit state. */
xe_i2c_pm_resume(xe, true);
regmap = devm_regmap_init(drm_dev, NULL, i2c, &i2c_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
i2c->bus_notifier.notifier_call = xe_i2c_notifier;
ret = bus_register_notifier(&i2c_bus_type, &i2c->bus_notifier);
if (ret)
return ret;
ret = xe_i2c_create_irq(i2c);
if (ret)
goto err_unregister_notifier;
ret = xe_i2c_register_adapter(i2c);
if (ret)
goto err_remove_irq;
return devm_add_action_or_reset(drm_dev, xe_i2c_remove, i2c);
err_remove_irq:
xe_i2c_remove_irq(i2c);
err_unregister_notifier:
bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
return ret;
}
+62
View File
@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: MIT */
#ifndef _XE_I2C_H_
#define _XE_I2C_H_
#include <linux/bits.h>
#include <linux/notifier.h>
#include <linux/types.h>
#include <linux/workqueue.h>
struct device;
struct fwnode_handle;
struct i2c_adapter;
struct i2c_client;
struct irq_domain;
struct platform_device;
struct xe_device;
struct xe_mmio;
#define XE_I2C_MAX_CLIENTS 3
#define XE_I2C_EP_COOKIE_DEVICE 0xde
/* Endpoint Capabilities */
#define XE_I2C_EP_CAP_IRQ BIT(0)
struct xe_i2c_endpoint {
u8 cookie;
u8 capabilities;
u16 addr[XE_I2C_MAX_CLIENTS];
};
struct xe_i2c {
struct fwnode_handle *adapter_node;
struct platform_device *pdev;
struct i2c_adapter *adapter;
struct i2c_client *client[XE_I2C_MAX_CLIENTS];
struct notifier_block bus_notifier;
struct work_struct work;
struct irq_domain *irqdomain;
int adapter_irq;
struct xe_i2c_endpoint ep;
struct device *drm_dev;
struct xe_mmio *mmio;
};
#if IS_ENABLED(CONFIG_I2C)
int xe_i2c_probe(struct xe_device *xe);
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl);
void xe_i2c_pm_suspend(struct xe_device *xe);
void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold);
#else
static inline int xe_i2c_probe(struct xe_device *xe) { return 0; }
static inline void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) { }
static inline void xe_i2c_pm_suspend(struct xe_device *xe) { }
static inline void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold) { }
#endif
#endif
+2
View File
@@ -18,6 +18,7 @@
#include "xe_gt.h"
#include "xe_guc.h"
#include "xe_hw_engine.h"
#include "xe_i2c.h"
#include "xe_memirq.h"
#include "xe_mmio.h"
#include "xe_pxp.h"
@@ -476,6 +477,7 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
if (xe->info.has_heci_cscfi)
xe_heci_csc_irq_handler(xe, master_ctl);
xe_display_irq_handler(xe, master_ctl);
xe_i2c_irq_handler(xe, master_ctl);
gu_misc_iir = gu_misc_irq_ack(xe, master_ctl);
}
}
+13 -2
View File
@@ -78,6 +78,9 @@ static struct xe_lmtt_pt *lmtt_pt_alloc(struct xe_lmtt *lmtt, unsigned int level
}
lmtt_assert(lmtt, xe_bo_is_vram(bo));
lmtt_debug(lmtt, "level=%u addr=%#llx\n", level, (u64)xe_bo_main_addr(bo, XE_PAGE_SIZE));
xe_map_memset(lmtt_to_xe(lmtt), &bo->vmap, 0, 0, xe_bo_size(bo));
pt->level = level;
pt->bo = bo;
@@ -91,6 +94,9 @@ out:
static void lmtt_pt_free(struct xe_lmtt_pt *pt)
{
lmtt_debug(&pt->bo->tile->sriov.pf.lmtt, "level=%u addr=%llx\n",
pt->level, (u64)xe_bo_main_addr(pt->bo, XE_PAGE_SIZE));
xe_bo_unpin_map_no_vm(pt->bo);
kfree(pt);
}
@@ -226,9 +232,14 @@ static void lmtt_write_pte(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pt,
switch (lmtt->ops->lmtt_pte_size(level)) {
case sizeof(u32):
lmtt_assert(lmtt, !overflows_type(pte, u32));
lmtt_assert(lmtt, !pte || !iosys_map_rd(&pt->bo->vmap, idx * sizeof(u32), u32));
xe_map_wr(lmtt_to_xe(lmtt), &pt->bo->vmap, idx * sizeof(u32), u32, pte);
break;
case sizeof(u64):
lmtt_assert(lmtt, !pte || !iosys_map_rd(&pt->bo->vmap, idx * sizeof(u64), u64));
xe_map_wr(lmtt_to_xe(lmtt), &pt->bo->vmap, idx * sizeof(u64), u64, pte);
break;
default:
@@ -386,11 +397,11 @@ static void lmtt_insert_bo(struct xe_lmtt *lmtt, unsigned int vfid, struct xe_bo
u64 addr, vram_offset;
lmtt_assert(lmtt, IS_ALIGNED(start, page_size));
lmtt_assert(lmtt, IS_ALIGNED(bo->size, page_size));
lmtt_assert(lmtt, IS_ALIGNED(xe_bo_size(bo), page_size));
lmtt_assert(lmtt, xe_bo_is_vram(bo));
vram_offset = vram_region_gpu_offset(bo->ttm.resource);
xe_res_first(bo->ttm.resource, 0, bo->size, &cur);
xe_res_first(bo->ttm.resource, 0, xe_bo_size(bo), &cur);
while (cur.remaining) {
addr = xe_res_dma(&cur);
addr += vram_offset; /* XXX */
+18 -23
View File
@@ -717,8 +717,12 @@ static u32 __xe_lrc_ctx_timestamp_udw_offset(struct xe_lrc *lrc)
static inline u32 __xe_lrc_indirect_ring_offset(struct xe_lrc *lrc)
{
/* Indirect ring state page is at the very end of LRC */
return lrc->size - LRC_INDIRECT_RING_STATE_SIZE;
return xe_bo_size(lrc->bo) - LRC_WA_BB_SIZE - LRC_INDIRECT_RING_STATE_SIZE;
}
static inline u32 __xe_lrc_wa_bb_offset(struct xe_lrc *lrc)
{
return xe_bo_size(lrc->bo) - LRC_WA_BB_SIZE;
}
#define DECL_MAP_ADDR_HELPERS(elem) \
@@ -973,11 +977,6 @@ struct wa_bb_setup {
u32 *batch, size_t max_size);
};
static size_t wa_bb_offset(struct xe_lrc *lrc)
{
return lrc->bo->size - LRC_WA_BB_SIZE;
}
static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
{
const size_t max_size = LRC_WA_BB_SIZE;
@@ -993,7 +992,7 @@ static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
return -ENOMEM;
cmd = buf;
} else {
cmd = lrc->bo->vmap.vaddr + wa_bb_offset(lrc);
cmd = lrc->bo->vmap.vaddr + __xe_lrc_wa_bb_offset(lrc);
}
remain = max_size / sizeof(*cmd);
@@ -1017,13 +1016,13 @@ static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
if (buf) {
xe_map_memcpy_to(gt_to_xe(lrc->gt), &lrc->bo->vmap,
wa_bb_offset(lrc), buf,
__xe_lrc_wa_bb_offset(lrc), buf,
(cmd - buf) * sizeof(*cmd));
kfree(buf);
}
xe_lrc_write_ctx_reg(lrc, CTX_BB_PER_CTX_PTR, xe_bo_ggtt_addr(lrc->bo) +
wa_bb_offset(lrc) + 1);
__xe_lrc_wa_bb_offset(lrc) + 1);
return 0;
@@ -1040,19 +1039,22 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
u32 init_flags)
{
struct xe_gt *gt = hwe->gt;
const u32 lrc_size = xe_gt_lrc_size(gt, hwe->class);
const u32 bo_size = ring_size + lrc_size + LRC_WA_BB_SIZE;
struct xe_tile *tile = gt_to_tile(gt);
struct xe_device *xe = gt_to_xe(gt);
struct iosys_map map;
void *init_data = NULL;
u32 arb_enable;
u32 lrc_size;
u32 bo_flags;
int err;
kref_init(&lrc->refcount);
lrc->gt = gt;
lrc->size = lrc_size;
lrc->flags = 0;
lrc_size = ring_size + xe_gt_lrc_size(gt, hwe->class);
lrc->ring.size = ring_size;
lrc->ring.tail = 0;
if (xe_gt_has_indirect_ring_state(gt))
lrc->flags |= XE_LRC_FLAG_INDIRECT_RING_STATE;
@@ -1065,17 +1067,12 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
* FIXME: Perma-pinning LRC as we don't yet support moving GGTT address
* via VM bind calls.
*/
lrc->bo = xe_bo_create_pin_map(xe, tile, NULL,
lrc_size + LRC_WA_BB_SIZE,
lrc->bo = xe_bo_create_pin_map(xe, tile, NULL, bo_size,
ttm_bo_type_kernel,
bo_flags);
if (IS_ERR(lrc->bo))
return PTR_ERR(lrc->bo);
lrc->size = lrc_size;
lrc->ring.size = ring_size;
lrc->ring.tail = 0;
xe_hw_fence_ctx_init(&lrc->fence_ctx, hwe->gt,
hwe->fence_irq, hwe->name);
@@ -1096,10 +1093,9 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
xe_map_memset(xe, &map, 0, 0, LRC_PPHWSP_SIZE); /* PPHWSP */
xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE,
gt->default_lrc[hwe->class] + LRC_PPHWSP_SIZE,
xe_gt_lrc_size(gt, hwe->class) - LRC_PPHWSP_SIZE);
lrc_size - LRC_PPHWSP_SIZE);
} else {
xe_map_memcpy_to(xe, &map, 0, init_data,
xe_gt_lrc_size(gt, hwe->class));
xe_map_memcpy_to(xe, &map, 0, init_data, lrc_size);
kfree(init_data);
}
@@ -1859,8 +1855,7 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc)
snapshot->seqno = xe_lrc_seqno(lrc);
snapshot->lrc_bo = xe_bo_get(lrc->bo);
snapshot->lrc_offset = xe_lrc_pphwsp_offset(lrc);
snapshot->lrc_size = lrc->bo->size - snapshot->lrc_offset -
LRC_WA_BB_SIZE;
snapshot->lrc_size = lrc->size;
snapshot->lrc_snapshot = NULL;
snapshot->ctx_timestamp = lower_32_bits(xe_lrc_ctx_timestamp(lrc));
snapshot->ctx_job_timestamp = xe_lrc_ctx_job_timestamp(lrc);
+1 -1
View File
@@ -22,7 +22,7 @@ struct xe_lrc {
*/
struct xe_bo *bo;
/** @size: size of lrc including any indirect ring state page */
/** @size: size of the lrc and optional indirect ring state */
u32 size;
/** @gt: gt which this LRC belongs to */
+26 -24
View File
@@ -82,7 +82,7 @@ struct xe_migrate {
* of the instruction. Subtracting the instruction header (1 dword) and
* address (2 dwords), that leaves 0x3FD dwords (0x1FE qwords) for PTE values.
*/
#define MAX_PTE_PER_SDI 0x1FE
#define MAX_PTE_PER_SDI 0x1FEU
/**
* xe_tile_migrate_exec_queue() - Get this tile's migrate exec queue.
@@ -203,7 +203,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
BUILD_BUG_ON(!(NUM_KERNEL_PDE & 1));
/* Need to be sure everything fits in the first PT, or create more */
xe_tile_assert(tile, m->batch_base_ofs + batch->size < SZ_2M);
xe_tile_assert(tile, m->batch_base_ofs + xe_bo_size(batch) < SZ_2M);
bo = xe_bo_create_pin_map(vm->xe, tile, vm,
num_entries * XE_PAGE_SIZE,
@@ -214,7 +214,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
return PTR_ERR(bo);
/* PT30 & PT31 reserved for 2M identity map */
pt29_ofs = bo->size - 3 * XE_PAGE_SIZE;
pt29_ofs = xe_bo_size(bo) - 3 * XE_PAGE_SIZE;
entry = vm->pt_ops->pde_encode_bo(bo, pt29_ofs, pat_index);
xe_pt_write(xe, &vm->pt_root[id]->bo->vmap, 0, entry);
@@ -236,7 +236,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
if (!IS_DGFX(xe)) {
/* Write out batch too */
m->batch_base_ofs = NUM_PT_SLOTS * XE_PAGE_SIZE;
for (i = 0; i < batch->size;
for (i = 0; i < xe_bo_size(batch);
i += vm->flags & XE_VM_FLAG_64K ? XE_64K_PAGE_SIZE :
XE_PAGE_SIZE) {
entry = vm->pt_ops->pte_encode_bo(batch, i,
@@ -247,13 +247,13 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
level++;
}
if (xe->info.has_usm) {
xe_tile_assert(tile, batch->size == SZ_1M);
xe_tile_assert(tile, xe_bo_size(batch) == SZ_1M);
batch = tile->primary_gt->usm.bb_pool->bo;
m->usm_batch_base_ofs = m->batch_base_ofs + SZ_1M;
xe_tile_assert(tile, batch->size == SZ_512K);
xe_tile_assert(tile, xe_bo_size(batch) == SZ_512K);
for (i = 0; i < batch->size;
for (i = 0; i < xe_bo_size(batch);
i += vm->flags & XE_VM_FLAG_64K ? XE_64K_PAGE_SIZE :
XE_PAGE_SIZE) {
entry = vm->pt_ops->pte_encode_bo(batch, i,
@@ -306,7 +306,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
/* Identity map the entire vram at 256GiB offset */
if (IS_DGFX(xe)) {
u64 pt30_ofs = bo->size - 2 * XE_PAGE_SIZE;
u64 pt30_ofs = xe_bo_size(bo) - 2 * XE_PAGE_SIZE;
xe_migrate_program_identity(xe, vm, bo, map_ofs, IDENTITY_OFFSET,
pat_index, pt30_ofs);
@@ -321,7 +321,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
u16 comp_pat_index = xe->pat.idx[XE_CACHE_NONE_COMPRESSION];
u64 vram_offset = IDENTITY_OFFSET +
DIV_ROUND_UP_ULL(xe->mem.vram.actual_physical_size, SZ_1G);
u64 pt31_ofs = bo->size - XE_PAGE_SIZE;
u64 pt31_ofs = xe_bo_size(bo) - XE_PAGE_SIZE;
xe_assert(xe, xe->mem.vram.actual_physical_size <= (MAX_NUM_PTE -
IDENTITY_OFFSET - IDENTITY_OFFSET / 2) * SZ_1G);
@@ -768,7 +768,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
struct xe_gt *gt = m->tile->primary_gt;
struct xe_device *xe = gt_to_xe(gt);
struct dma_fence *fence = NULL;
u64 size = src_bo->size;
u64 size = xe_bo_size(src_bo);
struct xe_res_cursor src_it, dst_it, ccs_it;
u64 src_L0_ofs, dst_L0_ofs;
u32 src_L0_pt, dst_L0_pt;
@@ -791,7 +791,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
if (XE_WARN_ON(copy_ccs && src_bo != dst_bo))
return ERR_PTR(-EINVAL);
if (src_bo != dst_bo && XE_WARN_ON(src_bo->size != dst_bo->size))
if (src_bo != dst_bo && XE_WARN_ON(xe_bo_size(src_bo) != xe_bo_size(dst_bo)))
return ERR_PTR(-EINVAL);
if (!src_is_vram)
@@ -863,7 +863,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
if (src_is_vram && xe_migrate_allow_identity(src_L0, &src_it))
xe_res_next(&src_it, src_L0);
else
emit_pte(m, bb, src_L0_pt, src_is_vram, copy_system_ccs,
emit_pte(m, bb, src_L0_pt, src_is_vram, copy_system_ccs || use_comp_pat,
&src_it, src_L0, src);
if (dst_is_vram && xe_migrate_allow_identity(src_L0, &dst_it))
@@ -1064,7 +1064,7 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
struct xe_device *xe = gt_to_xe(gt);
bool clear_only_system_ccs = false;
struct dma_fence *fence = NULL;
u64 size = bo->size;
u64 size = xe_bo_size(bo);
struct xe_res_cursor src_it;
struct ttm_resource *src = dst;
int err;
@@ -1076,9 +1076,9 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
clear_only_system_ccs = true;
if (!clear_vram)
xe_res_first_sg(xe_bo_sg(bo), 0, bo->size, &src_it);
xe_res_first_sg(xe_bo_sg(bo), 0, xe_bo_size(bo), &src_it);
else
xe_res_first(src, 0, bo->size, &src_it);
xe_res_first(src, 0, xe_bo_size(bo), &src_it);
while (size) {
u64 clear_L0_ofs;
@@ -1407,7 +1407,7 @@ __xe_migrate_update_pgtables(struct xe_migrate *m,
if (idx == chunk)
goto next_cmd;
xe_tile_assert(tile, pt_bo->size == SZ_4K);
xe_tile_assert(tile, xe_bo_size(pt_bo) == SZ_4K);
/* Map a PT at most once */
if (pt_bo->update_index < 0)
@@ -1553,15 +1553,17 @@ static u32 pte_update_cmd_size(u64 size)
u64 entries = DIV_U64_ROUND_UP(size, XE_PAGE_SIZE);
XE_WARN_ON(size > MAX_PREEMPTDISABLE_TRANSFER);
/*
* MI_STORE_DATA_IMM command is used to update page table. Each
* instruction can update maximumly 0x1ff pte entries. To update
* n (n <= 0x1ff) pte entries, we need:
* 1 dword for the MI_STORE_DATA_IMM command header (opcode etc)
* 2 dword for the page table's physical location
* 2*n dword for value of pte to fill (each pte entry is 2 dwords)
* instruction can update maximumly MAX_PTE_PER_SDI pte entries. To
* update n (n <= MAX_PTE_PER_SDI) pte entries, we need:
*
* - 1 dword for the MI_STORE_DATA_IMM command header (opcode etc)
* - 2 dword for the page table's physical location
* - 2*n dword for value of pte to fill (each pte entry is 2 dwords)
*/
num_dword = (1 + 2) * DIV_U64_ROUND_UP(entries, 0x1ff);
num_dword = (1 + 2) * DIV_U64_ROUND_UP(entries, MAX_PTE_PER_SDI);
num_dword += entries * 2;
return num_dword;
@@ -1577,7 +1579,7 @@ static void build_pt_update_batch_sram(struct xe_migrate *m,
ptes = DIV_ROUND_UP(size, XE_PAGE_SIZE);
while (ptes) {
u32 chunk = min(0x1ffU, ptes);
u32 chunk = min(MAX_PTE_PER_SDI, ptes);
bb->cs[bb->len++] = MI_STORE_DATA_IMM | MI_SDI_NUM_QW(chunk);
bb->cs[bb->len++] = pt_offset;
@@ -1866,7 +1868,7 @@ int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo,
if (IS_ERR(dma_addr))
return PTR_ERR(dma_addr);
xe_res_first(bo->ttm.resource, offset, bo->size - offset, &cursor);
xe_res_first(bo->ttm.resource, offset, xe_bo_size(bo) - offset, &cursor);
do {
struct dma_fence *__fence;
+10 -6
View File
@@ -55,6 +55,7 @@ static void tiles_fini(void *arg)
static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size)
{
struct xe_tile *tile;
struct xe_gt *gt;
u8 id;
/*
@@ -67,7 +68,7 @@ static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size)
/* Possibly override number of tile based on configuration register */
if (!xe->info.skip_mtcfg) {
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
u8 tile_count;
u8 tile_count, gt_count;
u32 mtcfg;
/*
@@ -84,12 +85,15 @@ static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size)
xe->info.tile_count = tile_count;
/*
* FIXME: Needs some work for standalone media, but
* should be impossible with multi-tile for now:
* multi-tile platform with standalone media doesn't
* exist
* We've already setup gt_count according to the full
* tile count. Re-calculate it to only include the GTs
* that belong to the remaining tile(s).
*/
xe->info.gt_count = xe->info.tile_count;
gt_count = 0;
for_each_gt(gt, xe, id)
if (gt->info.id < tile_count * xe->info.max_gt_per_tile)
gt_count++;
xe->info.gt_count = gt_count;
}
}
+167
View File
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: MIT
/*
* Copyright(c) 2019-2025, Intel Corporation. All rights reserved.
*/
#include <linux/intel_dg_nvm_aux.h>
#include <linux/pci.h>
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_mmio.h"
#include "xe_nvm.h"
#include "regs/xe_gsc_regs.h"
#include "xe_sriov.h"
#define GEN12_GUNIT_NVM_BASE 0x00102040
#define GEN12_DEBUG_NVM_BASE 0x00101018
#define GEN12_CNTL_PROTECTED_NVM_REG 0x0010100C
#define GEN12_GUNIT_NVM_SIZE 0x80
#define GEN12_DEBUG_NVM_SIZE 0x4
#define NVM_NON_POSTED_ERASE_CHICKEN_BIT BIT(13)
#define HECI_FW_STATUS_2_NVM_ACCESS_MODE BIT(3)
static const struct intel_dg_nvm_region regions[INTEL_DG_NVM_REGIONS] = {
[0] = { .name = "DESCRIPTOR", },
[2] = { .name = "GSC", },
[9] = { .name = "PADDING", },
[11] = { .name = "OptionROM", },
[12] = { .name = "DAM", },
};
static void xe_nvm_release_dev(struct device *dev)
{
}
static bool xe_nvm_non_posted_erase(struct xe_device *xe)
{
struct xe_gt *gt = xe_root_mmio_gt(xe);
if (xe->info.platform != XE_BATTLEMAGE)
return false;
return !(xe_mmio_read32(&gt->mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) &
NVM_NON_POSTED_ERASE_CHICKEN_BIT);
}
static bool xe_nvm_writable_override(struct xe_device *xe)
{
struct xe_gt *gt = xe_root_mmio_gt(xe);
bool writable_override;
resource_size_t base;
switch (xe->info.platform) {
case XE_BATTLEMAGE:
base = DG2_GSC_HECI2_BASE;
break;
case XE_PVC:
base = PVC_GSC_HECI2_BASE;
break;
case XE_DG2:
base = DG2_GSC_HECI2_BASE;
break;
case XE_DG1:
base = DG1_GSC_HECI2_BASE;
break;
default:
drm_err(&xe->drm, "Unknown platform\n");
return true;
}
writable_override =
!(xe_mmio_read32(&gt->mmio, HECI_FWSTS2(base)) &
HECI_FW_STATUS_2_NVM_ACCESS_MODE);
if (writable_override)
drm_info(&xe->drm, "NVM access overridden by jumper\n");
return writable_override;
}
int xe_nvm_init(struct xe_device *xe)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
struct auxiliary_device *aux_dev;
struct intel_dg_nvm_dev *nvm;
int ret;
if (!xe->info.has_gsc_nvm)
return 0;
/* No access to internal NVM from VFs */
if (IS_SRIOV_VF(xe))
return 0;
/* Nvm pointer should be NULL here */
if (WARN_ON(xe->nvm))
return -EFAULT;
xe->nvm = kzalloc(sizeof(*nvm), GFP_KERNEL);
if (!xe->nvm)
return -ENOMEM;
nvm = xe->nvm;
nvm->writable_override = xe_nvm_writable_override(xe);
nvm->non_posted_erase = xe_nvm_non_posted_erase(xe);
nvm->bar.parent = &pdev->resource[0];
nvm->bar.start = GEN12_GUNIT_NVM_BASE + pdev->resource[0].start;
nvm->bar.end = nvm->bar.start + GEN12_GUNIT_NVM_SIZE - 1;
nvm->bar.flags = IORESOURCE_MEM;
nvm->bar.desc = IORES_DESC_NONE;
nvm->regions = regions;
nvm->bar2.parent = &pdev->resource[0];
nvm->bar2.start = GEN12_DEBUG_NVM_BASE + pdev->resource[0].start;
nvm->bar2.end = nvm->bar2.start + GEN12_DEBUG_NVM_SIZE - 1;
nvm->bar2.flags = IORESOURCE_MEM;
nvm->bar2.desc = IORES_DESC_NONE;
aux_dev = &nvm->aux_dev;
aux_dev->name = "nvm";
aux_dev->id = (pci_domain_nr(pdev->bus) << 16) | pci_dev_id(pdev);
aux_dev->dev.parent = &pdev->dev;
aux_dev->dev.release = xe_nvm_release_dev;
ret = auxiliary_device_init(aux_dev);
if (ret) {
drm_err(&xe->drm, "xe-nvm aux init failed %d\n", ret);
goto err;
}
ret = auxiliary_device_add(aux_dev);
if (ret) {
drm_err(&xe->drm, "xe-nvm aux add failed %d\n", ret);
auxiliary_device_uninit(aux_dev);
goto err;
}
return 0;
err:
kfree(nvm);
xe->nvm = NULL;
return ret;
}
void xe_nvm_fini(struct xe_device *xe)
{
struct intel_dg_nvm_dev *nvm = xe->nvm;
if (!xe->info.has_gsc_nvm)
return;
/* No access to internal NVM from VFs */
if (IS_SRIOV_VF(xe))
return;
/* Nvm pointer should not be NULL here */
if (WARN_ON(!nvm))
return;
auxiliary_device_delete(&nvm->aux_dev);
auxiliary_device_uninit(&nvm->aux_dev);
kfree(nvm);
xe->nvm = NULL;
}
+15
View File
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright(c) 2019-2025 Intel Corporation. All rights reserved.
*/
#ifndef __XE_NVM_H__
#define __XE_NVM_H__
struct xe_device;
int xe_nvm_init(struct xe_device *xe);
void xe_nvm_fini(struct xe_device *xe);
#endif
+5 -5
View File
@@ -403,7 +403,7 @@ static int xe_oa_append_reports(struct xe_oa_stream *stream, char __user *buf,
static void xe_oa_init_oa_buffer(struct xe_oa_stream *stream)
{
u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
int size_exponent = __ffs(stream->oa_buffer.bo->size);
int size_exponent = __ffs(xe_bo_size(stream->oa_buffer.bo));
u32 oa_buf = gtt_offset | OAG_OABUFFER_MEMORY_SELECT;
struct xe_mmio *mmio = &stream->gt->mmio;
unsigned long flags;
@@ -435,7 +435,7 @@ static void xe_oa_init_oa_buffer(struct xe_oa_stream *stream)
spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
/* Zero out the OA buffer since we rely on zero report id and timestamp fields */
memset(stream->oa_buffer.vaddr, 0, stream->oa_buffer.bo->size);
memset(stream->oa_buffer.vaddr, 0, xe_bo_size(stream->oa_buffer.bo));
}
static u32 __format_to_oactrl(const struct xe_oa_format *format, int counter_sel_mask)
@@ -1065,7 +1065,7 @@ static u32 oag_report_ctx_switches(const struct xe_oa_stream *stream)
static u32 oag_buf_size_select(const struct xe_oa_stream *stream)
{
return _MASKED_FIELD(OAG_OA_DEBUG_BUF_SIZE_SELECT,
stream->oa_buffer.bo->size > SZ_16M ?
xe_bo_size(stream->oa_buffer.bo) > SZ_16M ?
OAG_OA_DEBUG_BUF_SIZE_SELECT : 0);
}
@@ -1582,7 +1582,7 @@ static long xe_oa_status_locked(struct xe_oa_stream *stream, unsigned long arg)
static long xe_oa_info_locked(struct xe_oa_stream *stream, unsigned long arg)
{
struct drm_xe_oa_stream_info info = { .oa_buf_size = stream->oa_buffer.bo->size, };
struct drm_xe_oa_stream_info info = { .oa_buf_size = xe_bo_size(stream->oa_buffer.bo), };
void __user *uaddr = (void __user *)arg;
if (copy_to_user(uaddr, &info, sizeof(info)))
@@ -1668,7 +1668,7 @@ static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma)
}
/* Can mmap the entire OA buffer or nothing (no partial OA buffer mmaps) */
if (vma->vm_end - vma->vm_start != stream->oa_buffer.bo->size) {
if (vma->vm_end - vma->vm_start != xe_bo_size(stream->oa_buffer.bo)) {
drm_dbg(&stream->oa->xe->drm, "Wrong mmap size, must be OA buffer size\n");
return -EINVAL;
}
+28 -49
View File
@@ -38,43 +38,6 @@ enum toggle_d3cold {
D3COLD_ENABLE,
};
struct xe_subplatform_desc {
enum xe_subplatform subplatform;
const char *name;
const u16 *pciidlist;
};
struct xe_device_desc {
/* Should only ever be set for platforms without GMD_ID */
const struct xe_ip *pre_gmdid_graphics_ip;
/* Should only ever be set for platforms without GMD_ID */
const struct xe_ip *pre_gmdid_media_ip;
const char *platform_name;
const struct xe_subplatform_desc *subplatforms;
enum xe_platform platform;
u8 dma_mask_size;
u8 max_remote_tiles:2;
u8 require_force_probe:1;
u8 is_dgfx:1;
u8 has_display:1;
u8 has_fan_control:1;
u8 has_heci_gscfi:1;
u8 has_heci_cscfi:1;
u8 has_llc:1;
u8 has_mbx_power_limits:1;
u8 has_pxp:1;
u8 has_sriov:1;
u8 needs_scratch:1;
u8 skip_guc_pc:1;
u8 skip_mtcfg:1;
u8 skip_pcode:1;
};
__diag_push();
__diag_ignore_all("-Woverride-init", "Allow field overrides in table");
@@ -140,7 +103,6 @@ static const struct xe_graphics_desc graphics_xelpg = {
.has_asid = 1, \
.has_atomic_enable_pte_bit = 1, \
.has_flat_ccs = 1, \
.has_indirect_ring_state = 1, \
.has_range_tlb_invalidation = 1, \
.has_usm = 1, \
.has_64bit_timestamp = 1, \
@@ -184,6 +146,7 @@ static const struct xe_ip graphics_ips[] = {
{ 2004, "Xe2_LPG", &graphics_xe2 },
{ 3000, "Xe3_LPG", &graphics_xe2 },
{ 3001, "Xe3_LPG", &graphics_xe2 },
{ 3003, "Xe3_LPG", &graphics_xe2 },
};
/* Pre-GMDID Media IPs */
@@ -196,6 +159,7 @@ static const struct xe_ip media_ips[] = {
{ 1301, "Xe2_HPM", &media_xelpmp },
{ 2000, "Xe2_LPM", &media_xelpmp },
{ 3000, "Xe3_LPM", &media_xelpmp },
{ 3002, "Xe3_LPM", &media_xelpmp },
};
static const struct xe_device_desc tgl_desc = {
@@ -205,6 +169,7 @@ static const struct xe_device_desc tgl_desc = {
.dma_mask_size = 39,
.has_display = true,
.has_llc = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
};
@@ -215,6 +180,7 @@ static const struct xe_device_desc rkl_desc = {
.dma_mask_size = 39,
.has_display = true,
.has_llc = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
};
@@ -227,6 +193,7 @@ static const struct xe_device_desc adl_s_desc = {
.dma_mask_size = 39,
.has_display = true,
.has_llc = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
.subplatforms = (const struct xe_subplatform_desc[]) {
{ XE_SUBPLATFORM_ALDERLAKE_S_RPLS, "RPLS", adls_rpls_ids },
@@ -243,6 +210,7 @@ static const struct xe_device_desc adl_p_desc = {
.dma_mask_size = 39,
.has_display = true,
.has_llc = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
.subplatforms = (const struct xe_subplatform_desc[]) {
{ XE_SUBPLATFORM_ALDERLAKE_P_RPLU, "RPLU", adlp_rplu_ids },
@@ -257,6 +225,7 @@ static const struct xe_device_desc adl_n_desc = {
.dma_mask_size = 39,
.has_display = true,
.has_llc = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
};
@@ -270,7 +239,9 @@ static const struct xe_device_desc dg1_desc = {
PLATFORM(DG1),
.dma_mask_size = 39,
.has_display = true,
.has_gsc_nvm = 1,
.has_heci_gscfi = 1,
.max_gt_per_tile = 1,
.require_force_probe = true,
};
@@ -281,6 +252,7 @@ static const u16 dg2_g12_ids[] = { INTEL_DG2_G12_IDS(NOP), 0 };
#define DG2_FEATURES \
DGFX_FEATURES, \
PLATFORM(DG2), \
.has_gsc_nvm = 1, \
.has_heci_gscfi = 1, \
.subplatforms = (const struct xe_subplatform_desc[]) { \
{ XE_SUBPLATFORM_DG2_G10, "G10", dg2_g10_ids }, \
@@ -293,6 +265,7 @@ static const struct xe_device_desc ats_m_desc = {
.pre_gmdid_graphics_ip = &graphics_ip_xehpg,
.pre_gmdid_media_ip = &media_ip_xehpm,
.dma_mask_size = 46,
.max_gt_per_tile = 1,
.require_force_probe = true,
DG2_FEATURES,
@@ -303,6 +276,7 @@ static const struct xe_device_desc dg2_desc = {
.pre_gmdid_graphics_ip = &graphics_ip_xehpg,
.pre_gmdid_media_ip = &media_ip_xehpm,
.dma_mask_size = 46,
.max_gt_per_tile = 1,
.require_force_probe = true,
DG2_FEATURES,
@@ -317,7 +291,9 @@ static const __maybe_unused struct xe_device_desc pvc_desc = {
PLATFORM(PVC),
.dma_mask_size = 52,
.has_display = false,
.has_gsc_nvm = 1,
.has_heci_gscfi = 1,
.max_gt_per_tile = 1,
.max_remote_tiles = 1,
.require_force_probe = true,
.has_mbx_power_limits = false,
@@ -330,6 +306,7 @@ static const struct xe_device_desc mtl_desc = {
.dma_mask_size = 46,
.has_display = true,
.has_pxp = true,
.max_gt_per_tile = 2,
};
static const struct xe_device_desc lnl_desc = {
@@ -337,6 +314,7 @@ static const struct xe_device_desc lnl_desc = {
.dma_mask_size = 46,
.has_display = true,
.has_pxp = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
};
@@ -347,7 +325,9 @@ static const struct xe_device_desc bmg_desc = {
.has_display = true,
.has_fan_control = true,
.has_mbx_power_limits = true,
.has_gsc_nvm = 1,
.has_heci_cscfi = 1,
.max_gt_per_tile = 2,
.needs_scratch = true,
};
@@ -356,7 +336,7 @@ static const struct xe_device_desc ptl_desc = {
.dma_mask_size = 46,
.has_display = true,
.has_sriov = true,
.require_force_probe = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
};
@@ -590,6 +570,7 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.is_dgfx = desc->is_dgfx;
xe->info.has_fan_control = desc->has_fan_control;
xe->info.has_mbx_power_limits = desc->has_mbx_power_limits;
xe->info.has_gsc_nvm = desc->has_gsc_nvm;
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
xe->info.has_heci_cscfi = desc->has_heci_cscfi;
xe->info.has_llc = desc->has_llc;
@@ -603,6 +584,10 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
xe_modparam.probe_display &&
desc->has_display;
xe_assert(xe, desc->max_gt_per_tile > 0);
xe_assert(xe, desc->max_gt_per_tile <= XE_MAX_GT_PER_TILE);
xe->info.max_gt_per_tile = desc->max_gt_per_tile;
xe->info.tile_count = 1 + desc->max_remote_tiles;
err = xe_tile_init_early(xe_device_get_root_tile(xe), xe, 0);
@@ -702,10 +687,11 @@ static int xe_info_init(struct xe_device *xe,
*/
for_each_tile(tile, xe, id) {
gt = tile->primary_gt;
gt->info.id = xe->info.gt_count++;
gt->info.type = XE_GT_TYPE_MAIN;
gt->info.id = tile->id * xe->info.max_gt_per_tile;
gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state;
gt->info.engine_mask = graphics_desc->hw_engine_mask;
xe->info.gt_count++;
if (MEDIA_VER(xe) < 13 && media_desc)
gt->info.engine_mask |= media_desc->hw_engine_mask;
@@ -723,17 +709,10 @@ static int xe_info_init(struct xe_device *xe,
gt = tile->media_gt;
gt->info.type = XE_GT_TYPE_MEDIA;
gt->info.id = tile->id * xe->info.max_gt_per_tile + 1;
gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state;
gt->info.engine_mask = media_desc->hw_engine_mask;
/*
* FIXME: At the moment multi-tile and standalone media are
* mutually exclusive on current platforms. We'll need to
* come up with a better way to number GTs if we ever wind
* up with platforms that support both together.
*/
drm_WARN_ON(&xe->drm, id != 0);
gt->info.id = xe->info.gt_count++;
xe->info.gt_count++;
}
return 0;
+41
View File
@@ -8,6 +8,47 @@
#include <linux/types.h>
#include "xe_platform_types.h"
struct xe_subplatform_desc {
enum xe_subplatform subplatform;
const char *name;
const u16 *pciidlist;
};
struct xe_device_desc {
/* Should only ever be set for platforms without GMD_ID */
const struct xe_ip *pre_gmdid_graphics_ip;
/* Should only ever be set for platforms without GMD_ID */
const struct xe_ip *pre_gmdid_media_ip;
const char *platform_name;
const struct xe_subplatform_desc *subplatforms;
enum xe_platform platform;
u8 dma_mask_size;
u8 max_remote_tiles:2;
u8 max_gt_per_tile:2;
u8 require_force_probe:1;
u8 is_dgfx:1;
u8 has_display:1;
u8 has_fan_control:1;
u8 has_gsc_nvm:1;
u8 has_heci_gscfi:1;
u8 has_heci_cscfi:1;
u8 has_llc:1;
u8 has_mbx_power_limits:1;
u8 has_pxp:1;
u8 has_sriov:1;
u8 needs_scratch:1;
u8 skip_guc_pc:1;
u8 skip_mtcfg:1;
u8 skip_pcode:1;
};
struct xe_graphics_desc {
u8 va_bits;
u8 vm_max_level;
+15
View File
@@ -50,6 +50,21 @@
#define READ_PL_FROM_FW 0x1
#define READ_PL_FROM_PCODE 0x0
#define PCODE_LATE_BINDING 0x5C
#define GET_CAPABILITY_STATUS 0x0
#define V1_FAN_SUPPORTED REG_BIT(0)
#define VR_PARAMS_SUPPORTED REG_BIT(3)
#define V1_FAN_PROVISIONED REG_BIT(16)
#define VR_PARAMS_PROVISIONED REG_BIT(19)
#define GET_VERSION_LOW 0x1
#define GET_VERSION_HIGH 0x2
#define MAJOR_VERSION_MASK REG_GENMASK(31, 16)
#define MINOR_VERSION_MASK REG_GENMASK(15, 0)
#define HOTFIX_VERSION_MASK REG_GENMASK(31, 16)
#define BUILD_VERSION_MASK REG_GENMASK(15, 0)
#define FAN_TABLE 1
#define VR_CONFIG 2
#define PCODE_FREQUENCY_CONFIG 0x6e
/* Frequency Config Sub Commands (param1) */
#define PCODE_MBOX_FC_SC_READ_FUSED_P0 0x0
+15 -5
View File
@@ -19,6 +19,7 @@
#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_guc.h"
#include "xe_i2c.h"
#include "xe_irq.h"
#include "xe_pcode.h"
#include "xe_pxp.h"
@@ -134,7 +135,7 @@ int xe_pm_suspend(struct xe_device *xe)
/* FIXME: Super racey... */
err = xe_bo_evict_all(xe);
if (err)
goto err_pxp;
goto err_display;
for_each_gt(gt, xe, id) {
err = xe_gt_suspend(gt);
@@ -146,12 +147,13 @@ int xe_pm_suspend(struct xe_device *xe)
xe_display_pm_suspend_late(xe);
xe_i2c_pm_suspend(xe);
drm_dbg(&xe->drm, "Device suspended\n");
return 0;
err_display:
xe_display_pm_resume(xe);
err_pxp:
xe_pxp_pm_resume(xe->pxp);
err:
drm_dbg(&xe->drm, "Device suspend failed %d\n", err);
@@ -191,6 +193,8 @@ int xe_pm_resume(struct xe_device *xe)
if (err)
goto err;
xe_i2c_pm_resume(xe, xe->d3cold.allowed);
xe_irq_resume(xe);
for_each_gt(gt, xe, id)
@@ -488,6 +492,8 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
xe_display_pm_runtime_suspend_late(xe);
xe_i2c_pm_suspend(xe);
xe_rpm_lockmap_release(xe);
xe_pm_write_callback_task(xe, NULL);
return 0;
@@ -535,6 +541,8 @@ int xe_pm_runtime_resume(struct xe_device *xe)
goto out;
}
xe_i2c_pm_resume(xe, xe->d3cold.allowed);
xe_irq_resume(xe);
for_each_gt(gt, xe, id)
@@ -753,11 +761,13 @@ void xe_pm_assert_unbounded_bridge(struct xe_device *xe)
}
/**
* xe_pm_set_vram_threshold - Set a vram threshold for allowing/blocking D3Cold
* xe_pm_set_vram_threshold - Set a VRAM threshold for allowing/blocking D3Cold
* @xe: xe device instance
* @threshold: VRAM size in bites for the D3cold threshold
* @threshold: VRAM size in MiB for the D3cold threshold
*
* Returns 0 for success, negative error code otherwise.
* Return:
* * 0 - success
* * -EINVAL - invalid argument
*/
int xe_pm_set_vram_threshold(struct xe_device *xe, u32 threshold)
{
+5 -2
View File
@@ -157,10 +157,13 @@ static bool event_gt_forcewake(struct perf_event *event)
return true;
}
static bool event_supported(struct xe_pmu *pmu, unsigned int gt,
static bool event_supported(struct xe_pmu *pmu, unsigned int gt_id,
unsigned int id)
{
if (gt >= XE_MAX_GT_PER_TILE)
struct xe_device *xe = container_of(pmu, typeof(*xe), pmu);
struct xe_gt *gt = xe_device_get_gt(xe, gt_id);
if (!gt)
return false;
return id < sizeof(pmu->supported_events) * BITS_PER_BYTE &&
+16 -13
View File
@@ -141,7 +141,7 @@ query_engine_cycles(struct xe_device *xe,
return -EINVAL;
eci = &resp.eci;
if (eci->gt_id >= XE_MAX_GT_PER_TILE)
if (eci->gt_id >= xe->info.max_gt_per_tile)
return -EINVAL;
gt = xe_device_get_gt(xe, eci->gt_id);
@@ -368,6 +368,7 @@ static int query_gt_list(struct xe_device *xe, struct drm_xe_device_query *query
struct drm_xe_query_gt_list __user *query_ptr =
u64_to_user_ptr(query->data);
struct drm_xe_query_gt_list *gt_list;
int iter = 0;
u8 id;
if (query->size == 0) {
@@ -385,12 +386,12 @@ static int query_gt_list(struct xe_device *xe, struct drm_xe_device_query *query
for_each_gt(gt, xe, id) {
if (xe_gt_is_media_type(gt))
gt_list->gt_list[id].type = DRM_XE_QUERY_GT_TYPE_MEDIA;
gt_list->gt_list[iter].type = DRM_XE_QUERY_GT_TYPE_MEDIA;
else
gt_list->gt_list[id].type = DRM_XE_QUERY_GT_TYPE_MAIN;
gt_list->gt_list[id].tile_id = gt_to_tile(gt)->id;
gt_list->gt_list[id].gt_id = gt->info.id;
gt_list->gt_list[id].reference_clock = gt->info.reference_clock;
gt_list->gt_list[iter].type = DRM_XE_QUERY_GT_TYPE_MAIN;
gt_list->gt_list[iter].tile_id = gt_to_tile(gt)->id;
gt_list->gt_list[iter].gt_id = gt->info.id;
gt_list->gt_list[iter].reference_clock = gt->info.reference_clock;
/*
* The mem_regions indexes in the mask below need to
* directly identify the struct
@@ -406,19 +407,21 @@ static int query_gt_list(struct xe_device *xe, struct drm_xe_device_query *query
* assumption.
*/
if (!IS_DGFX(xe))
gt_list->gt_list[id].near_mem_regions = 0x1;
gt_list->gt_list[iter].near_mem_regions = 0x1;
else
gt_list->gt_list[id].near_mem_regions =
gt_list->gt_list[iter].near_mem_regions =
BIT(gt_to_tile(gt)->id) << 1;
gt_list->gt_list[id].far_mem_regions = xe->info.mem_region_mask ^
gt_list->gt_list[id].near_mem_regions;
gt_list->gt_list[iter].far_mem_regions = xe->info.mem_region_mask ^
gt_list->gt_list[iter].near_mem_regions;
gt_list->gt_list[id].ip_ver_major =
gt_list->gt_list[iter].ip_ver_major =
REG_FIELD_GET(GMD_ID_ARCH_MASK, gt->info.gmdid);
gt_list->gt_list[id].ip_ver_minor =
gt_list->gt_list[iter].ip_ver_minor =
REG_FIELD_GET(GMD_ID_RELEASE_MASK, gt->info.gmdid);
gt_list->gt_list[id].ip_ver_rev =
gt_list->gt_list[iter].ip_ver_rev =
REG_FIELD_GET(GMD_ID_REVID, gt->info.gmdid);
iter++;
}
if (copy_to_user(query_ptr, gt_list, size)) {
+91 -104
View File
@@ -147,127 +147,113 @@ void xe_sriov_vf_init_early(struct xe_device *xe)
xe_sriov_info(xe, "migration not supported by this module version\n");
}
/**
* vf_post_migration_requery_guc - Re-query GuC for current VF provisioning.
static bool gt_vf_post_migration_needed(struct xe_gt *gt)
{
return test_bit(gt->info.id, &gt_to_xe(gt)->sriov.vf.migration.gt_flags);
}
/*
* Notify GuCs marked in flags about resource fixups apply finished.
* @xe: the &xe_device struct instance
* @gt_flags: flags marking to which GTs the notification shall be sent
*/
static int vf_post_migration_notify_resfix_done(struct xe_device *xe, unsigned long gt_flags)
{
struct xe_gt *gt;
unsigned int id;
int err = 0;
for_each_gt(gt, xe, id) {
if (!test_bit(id, &gt_flags))
continue;
/* skip asking GuC for RESFIX exit if new recovery request arrived */
if (gt_vf_post_migration_needed(gt))
continue;
err = xe_gt_sriov_vf_notify_resfix_done(gt);
if (err)
break;
clear_bit(id, &gt_flags);
}
if (gt_flags && !err)
drm_dbg(&xe->drm, "another recovery imminent, skipped some notifications\n");
return err;
}
static int vf_get_next_migrated_gt_id(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int id;
for_each_gt(gt, xe, id) {
if (test_and_clear_bit(id, &xe->sriov.vf.migration.gt_flags))
return id;
}
return -1;
}
/**
* Perform post-migration fixups on a single GT.
*
* After migration, we need to re-query all VF configuration to make sure
* they match previous provisioning. Note that most of VF provisioning
* shall be the same, except GGTT range, since GGTT is not virtualized per-VF.
* After migration, GuC needs to be re-queried for VF configuration to check
* if it matches previous provisioning. Most of VF provisioning shall be the
* same, except GGTT range, since GGTT is not virtualized per-VF. If GGTT
* range has changed, we have to perform fixups - shift all GGTT references
* used anywhere within the driver. After the fixups in this function succeed,
* it is allowed to ask the GuC bound to this GT to continue normal operation.
*
* Returns: 0 if the operation completed successfully, or a negative error
* code otherwise.
*/
static int vf_post_migration_requery_guc(struct xe_device *xe)
static int gt_vf_post_migration_fixups(struct xe_gt *gt)
{
struct xe_gt *gt;
unsigned int id;
int err, ret = 0;
s64 shift;
int err;
for_each_gt(gt, xe, id) {
err = xe_gt_sriov_vf_query_config(gt);
ret = ret ?: err;
}
return ret;
}
static void vf_post_migration_fixup_ctb(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int id;
xe_assert(xe, IS_SRIOV_VF(xe));
for_each_gt(gt, xe, id) {
s32 shift = xe_gt_sriov_vf_ggtt_shift(gt);
err = xe_gt_sriov_vf_query_config(gt);
if (err)
return err;
shift = xe_gt_sriov_vf_ggtt_shift(gt);
if (shift) {
xe_tile_sriov_vf_fixup_ggtt_nodes(gt_to_tile(gt), shift);
/* FIXME: add the recovery steps */
xe_guc_ct_fixup_messages_with_ggtt(&gt->uc.guc.ct, shift);
}
}
/*
* vf_post_migration_imminent - Check if post-restore recovery is coming.
* @xe: the &xe_device struct instance
*
* Return: True if migration recovery worker will soon be running. Any worker currently
* executing does not affect the result.
*/
static bool vf_post_migration_imminent(struct xe_device *xe)
{
return xe->sriov.vf.migration.gt_flags != 0 ||
work_pending(&xe->sriov.vf.migration.worker);
}
static bool vf_post_migration_fixup_ggtt_nodes(struct xe_device *xe)
{
bool need_fixups = false;
struct xe_tile *tile;
unsigned int id;
for_each_tile(tile, xe, id) {
struct xe_gt *gt = tile->primary_gt;
s64 shift;
shift = xe_gt_sriov_vf_ggtt_shift(gt);
if (shift) {
need_fixups = true;
xe_tile_sriov_vf_fixup_ggtt_nodes(tile, shift);
}
}
return need_fixups;
}
/*
* Notify all GuCs about resource fixups apply finished.
*/
static void vf_post_migration_notify_resfix_done(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int id;
for_each_gt(gt, xe, id) {
if (vf_post_migration_imminent(xe))
goto skip;
xe_gt_sriov_vf_notify_resfix_done(gt);
}
return;
skip:
drm_dbg(&xe->drm, "another recovery imminent, skipping notifications\n");
return 0;
}
static void vf_post_migration_recovery(struct xe_device *xe)
{
bool need_fixups;
int err;
unsigned long fixed_gts = 0;
int id, err;
drm_dbg(&xe->drm, "migration recovery in progress\n");
xe_pm_runtime_get(xe);
err = vf_post_migration_requery_guc(xe);
if (vf_post_migration_imminent(xe))
goto defer;
if (unlikely(err))
goto fail;
if (!vf_migration_supported(xe)) {
xe_sriov_err(xe, "migration not supported by this module version\n");
err = -ENOTRECOVERABLE;
goto fail;
}
need_fixups = vf_post_migration_fixup_ggtt_nodes(xe);
/* FIXME: add the recovery steps */
if (need_fixups)
vf_post_migration_fixup_ctb(xe);
while (id = vf_get_next_migrated_gt_id(xe), id >= 0) {
struct xe_gt *gt = xe_device_get_gt(xe, id);
err = gt_vf_post_migration_fixups(gt);
if (err)
goto fail;
set_bit(id, &fixed_gts);
}
err = vf_post_migration_notify_resfix_done(xe, fixed_gts);
if (err)
goto fail;
vf_post_migration_notify_resfix_done(xe);
xe_pm_runtime_put(xe);
drm_notice(&xe->drm, "migration recovery ended\n");
return;
defer:
xe_pm_runtime_put(xe);
drm_dbg(&xe->drm, "migration recovery deferred\n");
return;
fail:
xe_pm_runtime_put(xe);
drm_err(&xe->drm, "migration recovery failed (%pe)\n", ERR_PTR(err));
@@ -282,18 +268,23 @@ static void migration_worker_func(struct work_struct *w)
vf_post_migration_recovery(xe);
}
static bool vf_ready_to_recovery_on_all_gts(struct xe_device *xe)
/*
* Check if post-restore recovery is coming on any of GTs.
* @xe: the &xe_device struct instance
*
* Return: True if migration recovery worker will soon be running. Any worker currently
* executing does not affect the result.
*/
static bool vf_ready_to_recovery_on_any_gts(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int id;
for_each_gt(gt, xe, id) {
if (!test_bit(id, &xe->sriov.vf.migration.gt_flags)) {
xe_gt_sriov_dbg_verbose(gt, "still not ready to recover\n");
return false;
}
if (test_bit(id, &xe->sriov.vf.migration.gt_flags))
return true;
}
return true;
return false;
}
/**
@@ -308,13 +299,9 @@ void xe_sriov_vf_start_migration_recovery(struct xe_device *xe)
xe_assert(xe, IS_SRIOV_VF(xe));
if (!vf_ready_to_recovery_on_all_gts(xe))
if (!vf_ready_to_recovery_on_any_gts(xe))
return;
WRITE_ONCE(xe->sriov.vf.migration.gt_flags, 0);
/* Ensure other threads see that no flags are set now. */
smp_mb();
started = queue_work(xe->sriov.wq, &xe->sriov.vf.migration.worker);
drm_info(&xe->drm, "VF migration recovery %s\n", started ?
"scheduled" : "already in progress");
+2
View File
@@ -5,6 +5,7 @@
#include "xe_step.h"
#include <kunit/visibility.h>
#include <linux/bitfield.h>
#include "xe_device.h"
@@ -255,3 +256,4 @@ const char *xe_step_name(enum xe_step step)
return "**";
}
}
EXPORT_SYMBOL_IF_KUNIT(xe_step_name);
+11 -8
View File
@@ -14,6 +14,7 @@
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_heci_gsc.h"
#include "xe_i2c.h"
#include "xe_mmio.h"
#include "xe_pcode_api.h"
#include "xe_vsec.h"
@@ -173,20 +174,22 @@ static int enable_survivability_mode(struct pci_dev *pdev)
survivability->mode = true;
ret = xe_heci_gsc_init(xe);
if (ret) {
/*
* But if it fails, device can't enter survivability
* so move it back for correct error handling
*/
survivability->mode = false;
return ret;
}
if (ret)
goto err;
xe_vsec_init(xe);
ret = xe_i2c_probe(xe);
if (ret)
goto err;
dev_err(dev, "In Survivability Mode\n");
return 0;
err:
survivability->mode = false;
return ret;
}
/**
+75 -50
View File
@@ -3,13 +3,17 @@
* Copyright © 2024 Intel Corporation
*/
#include <drm/drm_drv.h>
#include "xe_bo.h"
#include "xe_gt_stats.h"
#include "xe_gt_tlb_invalidation.h"
#include "xe_migrate.h"
#include "xe_module.h"
#include "xe_pm.h"
#include "xe_pt.h"
#include "xe_svm.h"
#include "xe_tile.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_vm.h"
#include "xe_vm_types.h"
@@ -295,7 +299,7 @@ static void xe_svm_garbage_collector_work_func(struct work_struct *w)
up_write(&vm->lock);
}
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
static struct xe_vram_region *page_to_vr(struct page *page)
{
@@ -483,16 +487,18 @@ static int xe_svm_copy_to_ram(struct page **pages, dma_addr_t *dma_addr,
return xe_svm_copy(pages, dma_addr, npages, XE_SVM_COPY_TO_SRAM);
}
static struct xe_bo *to_xe_bo(struct drm_gpusvm_devmem *devmem_allocation)
static struct xe_bo *to_xe_bo(struct drm_pagemap_devmem *devmem_allocation)
{
return container_of(devmem_allocation, struct xe_bo, devmem_allocation);
}
static void xe_svm_devmem_release(struct drm_gpusvm_devmem *devmem_allocation)
static void xe_svm_devmem_release(struct drm_pagemap_devmem *devmem_allocation)
{
struct xe_bo *bo = to_xe_bo(devmem_allocation);
struct xe_device *xe = xe_bo_device(bo);
xe_bo_put_async(bo);
xe_pm_runtime_put(xe);
}
static u64 block_offset_to_pfn(struct xe_vram_region *vr, u64 offset)
@@ -505,7 +511,7 @@ static struct drm_buddy *tile_to_buddy(struct xe_tile *tile)
return &tile->mem.vram.ttm.mm;
}
static int xe_svm_populate_devmem_pfn(struct drm_gpusvm_devmem *devmem_allocation,
static int xe_svm_populate_devmem_pfn(struct drm_pagemap_devmem *devmem_allocation,
unsigned long npages, unsigned long *pfn)
{
struct xe_bo *bo = to_xe_bo(devmem_allocation);
@@ -528,7 +534,7 @@ static int xe_svm_populate_devmem_pfn(struct drm_gpusvm_devmem *devmem_allocatio
return 0;
}
static const struct drm_gpusvm_devmem_ops gpusvm_devmem_ops = {
static const struct drm_pagemap_devmem_ops dpagemap_devmem_ops = {
.devmem_release = xe_svm_devmem_release,
.populate_devmem_pfn = xe_svm_populate_devmem_pfn,
.copy_to_devmem = xe_svm_copy_to_devmem,
@@ -676,75 +682,69 @@ u64 xe_svm_find_vma_start(struct xe_vm *vm, u64 start, u64 end, struct xe_vma *v
min(end, xe_vma_end(vma)));
}
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
static struct xe_vram_region *tile_to_vr(struct xe_tile *tile)
{
return &tile->mem.vram;
}
/**
* xe_svm_alloc_vram()- Allocate device memory pages for range,
* migrating existing data.
* @vm: The VM.
* @tile: tile to allocate vram from
* @range: SVM range
* @ctx: DRM GPU SVM context
*
* Return: 0 on success, error code on failure.
*/
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
struct xe_svm_range *range,
const struct drm_gpusvm_ctx *ctx)
static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
unsigned long start, unsigned long end,
struct mm_struct *mm,
unsigned long timeslice_ms)
{
struct mm_struct *mm = vm->svm.gpusvm.mm;
struct xe_tile *tile = container_of(dpagemap, typeof(*tile), mem.vram.dpagemap);
struct xe_device *xe = tile_to_xe(tile);
struct device *dev = xe->drm.dev;
struct xe_vram_region *vr = tile_to_vr(tile);
struct drm_buddy_block *block;
struct list_head *blocks;
struct xe_bo *bo;
ktime_t end = 0;
int err;
ktime_t time_end = 0;
int err, idx;
range_debug(range, "ALLOCATE VRAM");
if (!drm_dev_enter(&xe->drm, &idx))
return -ENODEV;
if (!mmget_not_zero(mm))
return -EFAULT;
mmap_read_lock(mm);
xe_pm_runtime_get(xe);
retry:
bo = xe_bo_create_locked(tile_to_xe(tile), NULL, NULL,
xe_svm_range_size(range),
retry:
bo = xe_bo_create_locked(tile_to_xe(tile), NULL, NULL, end - start,
ttm_bo_type_device,
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_CPU_ADDR_MIRROR);
if (IS_ERR(bo)) {
err = PTR_ERR(bo);
if (xe_vm_validate_should_retry(NULL, err, &end))
if (xe_vm_validate_should_retry(NULL, err, &time_end))
goto retry;
goto unlock;
goto out_pm_put;
}
drm_gpusvm_devmem_init(&bo->devmem_allocation,
vm->xe->drm.dev, mm,
&gpusvm_devmem_ops,
&tile->mem.vram.dpagemap,
xe_svm_range_size(range));
drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm,
&dpagemap_devmem_ops,
&tile->mem.vram.dpagemap,
end - start);
blocks = &to_xe_ttm_vram_mgr_resource(bo->ttm.resource)->blocks;
list_for_each_entry(block, blocks, link)
block->private = vr;
xe_bo_get(bo);
err = drm_gpusvm_migrate_to_devmem(&vm->svm.gpusvm, &range->base,
&bo->devmem_allocation, ctx);
/* Ensure the device has a pm ref while there are device pages active. */
xe_pm_runtime_get_noresume(xe);
err = drm_pagemap_migrate_to_devmem(&bo->devmem_allocation, mm,
start, end, timeslice_ms,
xe_svm_devm_owner(xe));
if (err)
xe_svm_devmem_release(&bo->devmem_allocation);
xe_bo_unlock(bo);
xe_bo_put(bo);
unlock:
mmap_read_unlock(mm);
mmput(mm);
out_pm_put:
xe_pm_runtime_put(xe);
drm_dev_exit(idx);
return err;
}
@@ -810,13 +810,13 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
struct drm_gpusvm_ctx ctx = {
.read_only = xe_vma_read_only(vma),
.devmem_possible = IS_DGFX(vm->xe) &&
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR),
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP),
.check_pages_threshold = IS_DGFX(vm->xe) &&
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) ? SZ_64K : 0,
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) ? SZ_64K : 0,
.devmem_only = atomic && IS_DGFX(vm->xe) &&
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR),
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP),
.timeslice_ms = atomic && IS_DGFX(vm->xe) &&
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) ?
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) ?
vm->xe->atomic_svm_timeslice_ms : 0,
};
struct xe_svm_range *range;
@@ -852,7 +852,7 @@ retry:
if (--migrate_try_count >= 0 &&
xe_svm_range_needs_migrate_to_vram(range, vma, IS_DGFX(vm->xe))) {
err = xe_svm_alloc_vram(vm, tile, range, &ctx);
err = xe_svm_alloc_vram(tile, range, &ctx);
ctx.timeslice_ms <<= 1; /* Double timeslice if we have to retry */
if (err) {
if (migrate_try_count || !ctx.devmem_only) {
@@ -944,7 +944,7 @@ bool xe_svm_has_mapping(struct xe_vm *vm, u64 start, u64 end)
*/
int xe_svm_bo_evict(struct xe_bo *bo)
{
return drm_gpusvm_evict_to_ram(&bo->devmem_allocation);
return drm_pagemap_evict_to_ram(&bo->devmem_allocation);
}
/**
@@ -997,7 +997,31 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range,
return err;
}
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
/**
* xe_svm_alloc_vram()- Allocate device memory pages for range,
* migrating existing data.
* @tile: tile to allocate vram from
* @range: SVM range
* @ctx: DRM GPU SVM context
*
* Return: 0 on success, error code on failure.
*/
int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
const struct drm_gpusvm_ctx *ctx)
{
struct drm_pagemap *dpagemap;
xe_assert(tile_to_xe(tile), range->base.flags.migrate_devmem);
range_debug(range, "ALLOCATE VRAM");
dpagemap = xe_tile_local_pagemap(tile);
return drm_pagemap_populate_mm(dpagemap, xe_svm_range_start(range),
xe_svm_range_end(range),
range->base.gpusvm->mm,
ctx->timeslice_ms);
}
static struct drm_pagemap_device_addr
xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap,
@@ -1023,6 +1047,7 @@ xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap,
static const struct drm_pagemap_ops xe_drm_pagemap_ops = {
.device_map = xe_drm_pagemap_device_map,
.populate_mm = xe_drm_pagemap_populate_mm,
};
/**
@@ -1054,7 +1079,7 @@ int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
vr->pagemap.range.start = res->start;
vr->pagemap.range.end = res->end;
vr->pagemap.nr_range = 1;
vr->pagemap.ops = drm_gpusvm_pagemap_ops_get();
vr->pagemap.ops = drm_pagemap_pagemap_ops_get();
vr->pagemap.owner = xe_svm_devm_owner(xe);
addr = devm_memremap_pages(dev, &vr->pagemap);
@@ -1075,7 +1100,7 @@ int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
return 0;
}
#else
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
int xe_svm_alloc_vram(struct xe_tile *tile,
struct xe_svm_range *range,
const struct drm_gpusvm_ctx *ctx)
{
+4 -6
View File
@@ -70,8 +70,7 @@ int xe_svm_bo_evict(struct xe_bo *bo);
void xe_svm_range_debug(struct xe_svm_range *range, const char *operation);
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
struct xe_svm_range *range,
int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
const struct drm_gpusvm_ctx *ctx);
struct xe_svm_range *xe_svm_range_find_or_insert(struct xe_vm *vm, u64 addr,
@@ -237,10 +236,9 @@ void xe_svm_range_debug(struct xe_svm_range *range, const char *operation)
{
}
static inline
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
struct xe_svm_range *range,
const struct drm_gpusvm_ctx *ctx)
static inline int
xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
const struct drm_gpusvm_ctx *ctx)
{
return -EOPNOTSUPP;
}
+7
View File
@@ -10,6 +10,7 @@
#include "xe_device.h"
#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_memirq.h"
#include "xe_migrate.h"
#include "xe_pcode.h"
#include "xe_sa.h"
@@ -174,6 +175,12 @@ int xe_tile_init_noalloc(struct xe_tile *tile)
int xe_tile_init(struct xe_tile *tile)
{
int err;
err = xe_memirq_init(&tile->memirq);
if (err)
return err;
tile->mem.kernel_bb_pool = xe_sa_bo_manager_init(tile, SZ_1M, 16);
if (IS_ERR(tile->mem.kernel_bb_pool))
return PTR_ERR(tile->mem.kernel_bb_pool);
+11
View File
@@ -16,4 +16,15 @@ int xe_tile_init(struct xe_tile *tile);
void xe_tile_migrate_wait(struct xe_tile *tile);
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
static inline struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile)
{
return &tile->mem.vram.dpagemap;
}
#else
static inline struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile)
{
return NULL;
}
#endif
#endif
+2 -2
View File
@@ -33,7 +33,7 @@ DECLARE_EVENT_CLASS(xe_bo,
TP_fast_assign(
__assign_str(dev);
__entry->size = bo->size;
__entry->size = xe_bo_size(bo);
__entry->flags = bo->flags;
__entry->vm = bo->vm;
),
@@ -73,7 +73,7 @@ TRACE_EVENT(xe_bo_move,
TP_fast_assign(
__entry->bo = bo;
__entry->size = bo->size;
__entry->size = xe_bo_size(bo);
__assign_str(new_placement_name);
__assign_str(old_placement_name);
__assign_str(device_id);
+41 -37
View File
@@ -33,6 +33,22 @@ uc_to_xe(struct xe_uc *uc)
}
/* Should be called once at driver load only */
int xe_uc_init_noalloc(struct xe_uc *uc)
{
int ret;
ret = xe_guc_init_noalloc(&uc->guc);
if (ret)
goto err;
/* HuC and GSC have no early dependencies and will be initialized during xe_uc_init(). */
return 0;
err:
xe_gt_err(uc_to_gt(uc), "Failed to early initialize uC (%pe)\n", ERR_PTR(ret));
return ret;
}
int xe_uc_init(struct xe_uc *uc)
{
int ret;
@@ -56,15 +72,17 @@ int xe_uc_init(struct xe_uc *uc)
if (!xe_device_uc_enabled(uc_to_xe(uc)))
return 0;
if (IS_SRIOV_VF(uc_to_xe(uc)))
return 0;
if (!IS_SRIOV_VF(uc_to_xe(uc))) {
ret = xe_wopcm_init(&uc->wopcm);
if (ret)
goto err;
}
ret = xe_wopcm_init(&uc->wopcm);
ret = xe_guc_min_load_for_hwconfig(&uc->guc);
if (ret)
goto err;
return 0;
err:
xe_gt_err(uc_to_gt(uc), "Failed to initialize uC (%pe)\n", ERR_PTR(ret));
return ret;
@@ -126,28 +144,7 @@ int xe_uc_sanitize_reset(struct xe_uc *uc)
return uc_reset(uc);
}
/**
* xe_uc_init_hwconfig - minimally init Uc, read and parse hwconfig
* @uc: The UC object
*
* Return: 0 on success, negative error code on error.
*/
int xe_uc_init_hwconfig(struct xe_uc *uc)
{
int ret;
/* GuC submission not enabled, nothing to do */
if (!xe_device_uc_enabled(uc_to_xe(uc)))
return 0;
ret = xe_guc_min_load_for_hwconfig(&uc->guc);
if (ret)
return ret;
return 0;
}
static int vf_uc_init_hw(struct xe_uc *uc)
static int vf_uc_load_hw(struct xe_uc *uc)
{
int err;
@@ -161,22 +158,30 @@ static int vf_uc_init_hw(struct xe_uc *uc)
err = xe_gt_sriov_vf_connect(uc_to_gt(uc));
if (err)
return err;
goto err_out;
uc->guc.submission_state.enabled = true;
err = xe_gt_record_default_lrcs(uc_to_gt(uc));
err = xe_guc_opt_in_features_enable(&uc->guc);
if (err)
return err;
err = xe_gt_record_default_lrcs(uc_to_gt(uc));
if (err)
goto err_out;
return 0;
err_out:
xe_guc_sanitize(&uc->guc);
return err;
}
/*
* Should be called during driver load, after every GT reset, and after every
* suspend to reload / auth the firmwares.
*/
int xe_uc_init_hw(struct xe_uc *uc)
int xe_uc_load_hw(struct xe_uc *uc)
{
int ret;
@@ -185,7 +190,7 @@ int xe_uc_init_hw(struct xe_uc *uc)
return 0;
if (IS_SRIOV_VF(uc_to_xe(uc)))
return vf_uc_init_hw(uc);
return vf_uc_load_hw(uc);
ret = xe_huc_upload(&uc->huc);
if (ret)
@@ -201,15 +206,15 @@ int xe_uc_init_hw(struct xe_uc *uc)
ret = xe_gt_record_default_lrcs(uc_to_gt(uc));
if (ret)
return ret;
goto err_out;
ret = xe_guc_post_load_init(&uc->guc);
if (ret)
return ret;
goto err_out;
ret = xe_guc_pc_start(&uc->guc.pc);
if (ret)
return ret;
goto err_out;
xe_guc_engine_activity_enable_stats(&uc->guc);
@@ -221,11 +226,10 @@ int xe_uc_init_hw(struct xe_uc *uc)
xe_gsc_load_start(&uc->gsc);
return 0;
}
int xe_uc_fini_hw(struct xe_uc *uc)
{
return xe_uc_sanitize_reset(uc);
err_out:
xe_guc_sanitize(&uc->guc);
return ret;
}
int xe_uc_reset_prepare(struct xe_uc *uc)
+2 -3
View File
@@ -8,11 +8,10 @@
struct xe_uc;
int xe_uc_init_noalloc(struct xe_uc *uc);
int xe_uc_init(struct xe_uc *uc);
int xe_uc_init_hwconfig(struct xe_uc *uc);
int xe_uc_init_post_hwconfig(struct xe_uc *uc);
int xe_uc_init_hw(struct xe_uc *uc);
int xe_uc_fini_hw(struct xe_uc *uc);
int xe_uc_load_hw(struct xe_uc *uc);
void xe_uc_gucrc_disable(struct xe_uc *uc);
int xe_uc_reset_prepare(struct xe_uc *uc);
void xe_uc_stop_prepare(struct xe_uc *uc);
+5 -3
View File
@@ -115,10 +115,11 @@ struct fw_blobs_by_type {
#define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED
#define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \
fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 44, 1)) \
fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 44, 1)) \
fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 47, 0)) \
fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 45, 2)) \
fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \
fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \
fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 44, 1)) \
fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \
fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \
fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \
fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 44, 1)) \
@@ -127,6 +128,7 @@ struct fw_blobs_by_type {
fw_def(TIGERLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1))
#define XE_HUC_FIRMWARE_DEFS(fw_def, mmp_ver, no_ver) \
fw_def(PANTHERLAKE, GT_TYPE_ANY, no_ver(xe, huc, ptl)) \
fw_def(BATTLEMAGE, GT_TYPE_ANY, no_ver(xe, huc, bmg)) \
fw_def(LUNARLAKE, GT_TYPE_ANY, no_ver(xe, huc, lnl)) \
fw_def(METEORLAKE, GT_TYPE_ANY, no_ver(i915, huc_gsc, mtl)) \
+4 -4
View File
@@ -2913,7 +2913,7 @@ static int prefetch_ranges(struct xe_vm *vm, struct xe_vma_op *op)
if (xe_svm_range_needs_migrate_to_vram(svm_range, vma, region)) {
tile = &vm->xe->tiles[region_to_mem_type[region] - XE_PL_VRAM0];
err = xe_svm_alloc_vram(vm, tile, svm_range, &ctx);
err = xe_svm_alloc_vram(tile, svm_range, &ctx);
if (err) {
drm_dbg(&vm->xe->drm, "VRAM allocation failed, retry from userspace, asid=%u, gpusvm=%p, errno=%pe\n",
vm->usm.asid, &vm->svm.gpusvm, ERR_PTR(err));
@@ -3466,9 +3466,9 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo,
{
u16 coh_mode;
if (XE_IOCTL_DBG(xe, range > bo->size) ||
if (XE_IOCTL_DBG(xe, range > xe_bo_size(bo)) ||
XE_IOCTL_DBG(xe, obj_offset >
bo->size - range)) {
xe_bo_size(bo) - range)) {
return -EINVAL;
}
@@ -3771,7 +3771,7 @@ struct dma_fence *xe_vm_bind_kernel_bo(struct xe_vm *vm, struct xe_bo *bo,
xe_vma_ops_init(&vops, vm, q, NULL, 0);
ops = vm_bind_ioctl_ops_create(vm, &vops, bo, 0, addr, bo->size,
ops = vm_bind_ioctl_ops_create(vm, &vops, bo, 0, addr, xe_bo_size(bo),
DRM_XE_VM_BIND_OP_MAP, 0, 0,
vm->xe->pat.idx[cache_lvl]);
if (IS_ERR(ops)) {
+16
View File
@@ -285,6 +285,18 @@ static const struct xe_rtp_entry_sr gt_was[] = {
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), IECPUNIT_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
{ XE_RTP_NAME("16021865536"),
XE_RTP_RULES(MEDIA_VERSION(3002),
ENGINE_CLASS(VIDEO_DECODE)),
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), IECPUNIT_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
{ XE_RTP_NAME("16021867713"),
XE_RTP_RULES(MEDIA_VERSION(3002),
ENGINE_CLASS(VIDEO_DECODE)),
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
{ XE_RTP_NAME("14021486841"),
XE_RTP_RULES(MEDIA_VERSION(3000), MEDIA_STEP(A0, B0),
ENGINE_CLASS(VIDEO_DECODE)),
@@ -644,6 +656,10 @@ static const struct xe_rtp_entry_sr engine_was[] = {
XE_RTP_ACTIONS(SET(RING_PSMI_CTL(0), RC_SEMA_IDLE_MSG_DISABLE,
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
},
{ XE_RTP_NAME("14021402888"),
XE_RTP_RULES(GRAPHICS_VERSION(3003), FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
},
};
static const struct xe_rtp_entry_sr lrc_was[] = {
+8 -2
View File
@@ -34,14 +34,16 @@
14022293748 GRAPHICS_VERSION_RANGE(2001, 2002)
GRAPHICS_VERSION(2004)
GRAPHICS_VERSION_RANGE(3000, 3001)
GRAPHICS_VERSION(3003)
22019794406 GRAPHICS_VERSION_RANGE(2001, 2002)
GRAPHICS_VERSION(2004)
GRAPHICS_VERSION_RANGE(3000, 3001)
GRAPHICS_VERSION(3003)
22019338487 MEDIA_VERSION(2000)
GRAPHICS_VERSION(2001)
GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
MEDIA_VERSION(3000), MEDIA_STEP(A0, B0), FUNC(xe_rtp_match_not_sriov_vf)
22019338487_display PLATFORM(LUNARLAKE)
16023588340 GRAPHICS_VERSION(2001)
16023588340 GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
14019789679 GRAPHICS_VERSION(1255)
GRAPHICS_VERSION_RANGE(1270, 2004)
no_media_l3 MEDIA_VERSION(3000)
@@ -58,8 +60,12 @@ no_media_l3 MEDIA_VERSION(3000)
GRAPHICS_VERSION(1260), GRAPHICS_STEP(A0, B0)
16023105232 GRAPHICS_VERSION_RANGE(2001, 3001)
MEDIA_VERSION_RANGE(1301, 3000)
MEDIA_VERSION(3002)
GRAPHICS_VERSION(3003)
16026508708 GRAPHICS_VERSION_RANGE(1200, 3001)
MEDIA_VERSION_RANGE(1300, 3000)
MEDIA_VERSION(3002)
GRAPHICS_VERSION(3003)
# SoC workaround - currently applies to all platforms with the following
# primary GT GMDID
+12 -6
View File
@@ -101,7 +101,7 @@ static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
}
#endif
static int txgbe_i2c_request_regs(struct dw_i2c_dev *dev)
static int dw_i2c_get_parent_regmap(struct dw_i2c_dev *dev)
{
dev->map = dev_get_regmap(dev->dev->parent, NULL);
if (!dev->map)
@@ -123,12 +123,15 @@ static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
struct platform_device *pdev = to_platform_device(dev->dev);
int ret;
if (device_is_compatible(dev->dev, "intel,xe-i2c"))
return dw_i2c_get_parent_regmap(dev);
switch (dev->flags & MODEL_MASK) {
case MODEL_BAIKAL_BT1:
ret = bt1_i2c_request_regs(dev);
break;
case MODEL_WANGXUN_SP:
ret = txgbe_i2c_request_regs(dev);
ret = dw_i2c_get_parent_regmap(dev);
break;
default:
dev->base = devm_platform_ioremap_resource(pdev, 0);
@@ -205,25 +208,28 @@ static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev)
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
struct device *device = &pdev->dev;
struct i2c_adapter *adap;
struct dw_i2c_dev *dev;
int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
irq = platform_get_irq_optional(pdev, 0);
if (irq == -ENXIO)
flags |= ACCESS_POLLING;
else if (irq < 0)
return irq;
dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->flags = (uintptr_t)device_get_match_data(device);
if (device_property_present(device, "wx,i2c-snps-model"))
dev->flags = MODEL_WANGXUN_SP | ACCESS_POLLING;
flags = MODEL_WANGXUN_SP | ACCESS_POLLING;
dev->dev = device;
dev->irq = irq;
dev->flags = flags;
platform_set_drvdata(pdev, dev);
ret = dw_i2c_plat_request_regs(dev);
+11
View File
@@ -183,6 +183,17 @@ config MTD_POWERNV_FLASH
platforms from Linux. This device abstracts away the
firmware interface for flash access.
config MTD_INTEL_DG
tristate "Intel Discrete Graphics non-volatile memory driver"
depends on AUXILIARY_BUS
depends on MTD
help
This provides an MTD device to access Intel Discrete Graphics
non-volatile memory.
To compile this driver as a module, choose M here: the module
will be called mtd-intel-dg.
comment "Disk-On-Chip Device Drivers"
config MTD_DOCG3
+1
View File
@@ -18,6 +18,7 @@ obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
obj-$(CONFIG_MTD_POWERNV_FLASH) += powernv_flash.o
obj-$(CONFIG_MTD_INTEL_DG) += mtd_intel_dg.o
CFLAGS_docg3.o += -I$(src)
+830
View File
@@ -0,0 +1,830 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright(c) 2019-2025, Intel Corporation. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/intel_dg_nvm_aux.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/sizes.h>
#include <linux/types.h>
struct intel_dg_nvm {
struct kref refcnt;
struct mtd_info mtd;
struct mutex lock; /* region access lock */
void __iomem *base;
void __iomem *base2;
bool non_posted_erase;
size_t size;
unsigned int nregions;
struct {
const char *name;
u8 id;
u64 offset;
u64 size;
unsigned int is_readable:1;
unsigned int is_writable:1;
} regions[] __counted_by(nregions);
};
#define NVM_TRIGGER_REG 0x00000000
#define NVM_VALSIG_REG 0x00000010
#define NVM_ADDRESS_REG 0x00000040
#define NVM_REGION_ID_REG 0x00000044
#define NVM_DEBUG_REG 0x00000000
/*
* [15:0]-Erase size = 0x0010 4K 0x0080 32K 0x0100 64K
* [23:16]-Reserved
* [31:24]-Erase MEM RegionID
*/
#define NVM_ERASE_REG 0x00000048
#define NVM_ACCESS_ERROR_REG 0x00000070
#define NVM_ADDRESS_ERROR_REG 0x00000074
/* Flash Valid Signature */
#define NVM_FLVALSIG 0x0FF0A55A
#define NVM_MAP_ADDR_MASK GENMASK(7, 0)
#define NVM_MAP_ADDR_SHIFT 0x00000004
#define NVM_REGION_ID_DESCRIPTOR 0
/* Flash Region Base Address */
#define NVM_FRBA 0x40
/* Flash Region __n - Flash Descriptor Record */
#define NVM_FLREG(__n) (NVM_FRBA + ((__n) * 4))
/* Flash Map 1 Register */
#define NVM_FLMAP1_REG 0x18
#define NVM_FLMSTR4_OFFSET 0x00C
#define NVM_ACCESS_ERROR_PCIE_MASK 0x7
#define NVM_FREG_BASE_MASK GENMASK(15, 0)
#define NVM_FREG_ADDR_MASK GENMASK(31, 16)
#define NVM_FREG_ADDR_SHIFT 12
#define NVM_FREG_MIN_REGION_SIZE 0xFFF
#define NVM_NON_POSTED_ERASE_DONE BIT(23)
#define NVM_NON_POSTED_ERASE_DONE_ITER 3000
static inline void idg_nvm_set_region_id(struct intel_dg_nvm *nvm, u8 region)
{
iowrite32((u32)region, nvm->base + NVM_REGION_ID_REG);
}
static inline u32 idg_nvm_error(struct intel_dg_nvm *nvm)
{
void __iomem *base = nvm->base;
u32 reg = ioread32(base + NVM_ACCESS_ERROR_REG) & NVM_ACCESS_ERROR_PCIE_MASK;
/* reset error bits */
if (reg)
iowrite32(reg, base + NVM_ACCESS_ERROR_REG);
return reg;
}
static inline u32 idg_nvm_read32(struct intel_dg_nvm *nvm, u32 address)
{
void __iomem *base = nvm->base;
iowrite32(address, base + NVM_ADDRESS_REG);
return ioread32(base + NVM_TRIGGER_REG);
}
static inline u64 idg_nvm_read64(struct intel_dg_nvm *nvm, u32 address)
{
void __iomem *base = nvm->base;
iowrite32(address, base + NVM_ADDRESS_REG);
return readq(base + NVM_TRIGGER_REG);
}
static void idg_nvm_write32(struct intel_dg_nvm *nvm, u32 address, u32 data)
{
void __iomem *base = nvm->base;
iowrite32(address, base + NVM_ADDRESS_REG);
iowrite32(data, base + NVM_TRIGGER_REG);
}
static void idg_nvm_write64(struct intel_dg_nvm *nvm, u32 address, u64 data)
{
void __iomem *base = nvm->base;
iowrite32(address, base + NVM_ADDRESS_REG);
writeq(data, base + NVM_TRIGGER_REG);
}
static int idg_nvm_get_access_map(struct intel_dg_nvm *nvm, u32 *access_map)
{
u32 fmstr4_addr;
u32 fmstr4;
u32 flmap1;
u32 fmba;
idg_nvm_set_region_id(nvm, NVM_REGION_ID_DESCRIPTOR);
flmap1 = idg_nvm_read32(nvm, NVM_FLMAP1_REG);
if (idg_nvm_error(nvm))
return -EIO;
/* Get Flash Master Baser Address (FMBA) */
fmba = (FIELD_GET(NVM_MAP_ADDR_MASK, flmap1) << NVM_MAP_ADDR_SHIFT);
fmstr4_addr = fmba + NVM_FLMSTR4_OFFSET;
fmstr4 = idg_nvm_read32(nvm, fmstr4_addr);
if (idg_nvm_error(nvm))
return -EIO;
*access_map = fmstr4;
return 0;
}
/*
* Region read/write access encoded in the access map
* in the following order from the lower bit:
* [3:0] regions 12-15 read state
* [7:4] regions 12-15 write state
* [19:8] regions 0-11 read state
* [31:20] regions 0-11 write state
*/
static bool idg_nvm_region_readable(u32 access_map, u8 region)
{
if (region < 12)
return access_map & BIT(region + 8); /* [19:8] */
else
return access_map & BIT(region - 12); /* [3:0] */
}
static bool idg_nvm_region_writable(u32 access_map, u8 region)
{
if (region < 12)
return access_map & BIT(region + 20); /* [31:20] */
else
return access_map & BIT(region - 8); /* [7:4] */
}
static int idg_nvm_is_valid(struct intel_dg_nvm *nvm)
{
u32 is_valid;
idg_nvm_set_region_id(nvm, NVM_REGION_ID_DESCRIPTOR);
is_valid = idg_nvm_read32(nvm, NVM_VALSIG_REG);
if (idg_nvm_error(nvm))
return -EIO;
if (is_valid != NVM_FLVALSIG)
return -ENODEV;
return 0;
}
static unsigned int idg_nvm_get_region(const struct intel_dg_nvm *nvm, loff_t from)
{
unsigned int i;
for (i = 0; i < nvm->nregions; i++) {
if ((nvm->regions[i].offset + nvm->regions[i].size - 1) >= from &&
nvm->regions[i].offset <= from &&
nvm->regions[i].size != 0)
break;
}
return i;
}
static ssize_t idg_nvm_rewrite_partial(struct intel_dg_nvm *nvm, loff_t to,
loff_t offset, size_t len, const u32 *newdata)
{
u32 data = idg_nvm_read32(nvm, to);
if (idg_nvm_error(nvm))
return -EIO;
memcpy((u8 *)&data + offset, newdata, len);
idg_nvm_write32(nvm, to, data);
if (idg_nvm_error(nvm))
return -EIO;
return len;
}
static ssize_t idg_write(struct intel_dg_nvm *nvm, u8 region,
loff_t to, size_t len, const unsigned char *buf)
{
size_t len_s = len;
size_t to_shift;
size_t len8;
size_t len4;
ssize_t ret;
size_t to4;
size_t i;
idg_nvm_set_region_id(nvm, region);
to4 = ALIGN_DOWN(to, sizeof(u32));
to_shift = min(sizeof(u32) - ((size_t)to - to4), len);
if (to - to4) {
ret = idg_nvm_rewrite_partial(nvm, to4, to - to4, to_shift, (u32 *)&buf[0]);
if (ret < 0)
return ret;
buf += to_shift;
to += to_shift;
len_s -= to_shift;
}
if (!IS_ALIGNED(to, sizeof(u64)) &&
((to ^ (to + len_s)) & GENMASK(31, 10))) {
/*
* Workaround reads/writes across 1k-aligned addresses
* (start u32 before 1k, end u32 after)
* as this fails on hardware.
*/
u32 data;
memcpy(&data, &buf[0], sizeof(u32));
idg_nvm_write32(nvm, to, data);
if (idg_nvm_error(nvm))
return -EIO;
buf += sizeof(u32);
to += sizeof(u32);
len_s -= sizeof(u32);
}
len8 = ALIGN_DOWN(len_s, sizeof(u64));
for (i = 0; i < len8; i += sizeof(u64)) {
u64 data;
memcpy(&data, &buf[i], sizeof(u64));
idg_nvm_write64(nvm, to + i, data);
if (idg_nvm_error(nvm))
return -EIO;
}
len4 = len_s - len8;
if (len4 >= sizeof(u32)) {
u32 data;
memcpy(&data, &buf[i], sizeof(u32));
idg_nvm_write32(nvm, to + i, data);
if (idg_nvm_error(nvm))
return -EIO;
i += sizeof(u32);
len4 -= sizeof(u32);
}
if (len4 > 0) {
ret = idg_nvm_rewrite_partial(nvm, to + i, 0, len4, (u32 *)&buf[i]);
if (ret < 0)
return ret;
}
return len;
}
static ssize_t idg_read(struct intel_dg_nvm *nvm, u8 region,
loff_t from, size_t len, unsigned char *buf)
{
size_t len_s = len;
size_t from_shift;
size_t from4;
size_t len8;
size_t len4;
size_t i;
idg_nvm_set_region_id(nvm, region);
from4 = ALIGN_DOWN(from, sizeof(u32));
from_shift = min(sizeof(u32) - ((size_t)from - from4), len);
if (from - from4) {
u32 data = idg_nvm_read32(nvm, from4);
if (idg_nvm_error(nvm))
return -EIO;
memcpy(&buf[0], (u8 *)&data + (from - from4), from_shift);
len_s -= from_shift;
buf += from_shift;
from += from_shift;
}
if (!IS_ALIGNED(from, sizeof(u64)) &&
((from ^ (from + len_s)) & GENMASK(31, 10))) {
/*
* Workaround reads/writes across 1k-aligned addresses
* (start u32 before 1k, end u32 after)
* as this fails on hardware.
*/
u32 data = idg_nvm_read32(nvm, from);
if (idg_nvm_error(nvm))
return -EIO;
memcpy(&buf[0], &data, sizeof(data));
len_s -= sizeof(u32);
buf += sizeof(u32);
from += sizeof(u32);
}
len8 = ALIGN_DOWN(len_s, sizeof(u64));
for (i = 0; i < len8; i += sizeof(u64)) {
u64 data = idg_nvm_read64(nvm, from + i);
if (idg_nvm_error(nvm))
return -EIO;
memcpy(&buf[i], &data, sizeof(data));
}
len4 = len_s - len8;
if (len4 >= sizeof(u32)) {
u32 data = idg_nvm_read32(nvm, from + i);
if (idg_nvm_error(nvm))
return -EIO;
memcpy(&buf[i], &data, sizeof(data));
i += sizeof(u32);
len4 -= sizeof(u32);
}
if (len4 > 0) {
u32 data = idg_nvm_read32(nvm, from + i);
if (idg_nvm_error(nvm))
return -EIO;
memcpy(&buf[i], &data, len4);
}
return len;
}
static ssize_t
idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_addr)
{
void __iomem *base2 = nvm->base2;
void __iomem *base = nvm->base;
const u32 block = 0x10;
u32 iter = 0;
u32 reg;
u64 i;
for (i = 0; i < len; i += SZ_4K) {
iowrite32(from + i, base + NVM_ADDRESS_REG);
iowrite32(region << 24 | block, base + NVM_ERASE_REG);
if (nvm->non_posted_erase) {
/* Wait for Erase Done */
reg = ioread32(base2 + NVM_DEBUG_REG);
while (!(reg & NVM_NON_POSTED_ERASE_DONE) &&
++iter < NVM_NON_POSTED_ERASE_DONE_ITER) {
msleep(10);
reg = ioread32(base2 + NVM_DEBUG_REG);
}
if (reg & NVM_NON_POSTED_ERASE_DONE) {
/* Clear Erase Done */
iowrite32(reg, base2 + NVM_DEBUG_REG);
} else {
*fail_addr = from + i;
return -ETIME;
}
}
/* Since the writes are via sgunit
* we cannot do back to back erases.
*/
msleep(50);
}
return len;
}
static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
bool non_posted_erase)
{
u32 access_map = 0;
unsigned int i, n;
int ret;
/* clean error register, previous errors are ignored */
idg_nvm_error(nvm);
ret = idg_nvm_is_valid(nvm);
if (ret) {
dev_err(device, "The MEM is not valid %d\n", ret);
return ret;
}
if (idg_nvm_get_access_map(nvm, &access_map))
return -EIO;
for (i = 0, n = 0; i < nvm->nregions; i++) {
u32 address, base, limit, region;
u8 id = nvm->regions[i].id;
address = NVM_FLREG(id);
region = idg_nvm_read32(nvm, address);
base = FIELD_GET(NVM_FREG_BASE_MASK, region) << NVM_FREG_ADDR_SHIFT;
limit = (FIELD_GET(NVM_FREG_ADDR_MASK, region) << NVM_FREG_ADDR_SHIFT) |
NVM_FREG_MIN_REGION_SIZE;
dev_dbg(device, "[%d] %s: region: 0x%08X base: 0x%08x limit: 0x%08x\n",
id, nvm->regions[i].name, region, base, limit);
if (base >= limit || (i > 0 && limit == 0)) {
dev_dbg(device, "[%d] %s: disabled\n",
id, nvm->regions[i].name);
nvm->regions[i].is_readable = 0;
continue;
}
if (nvm->size < limit)
nvm->size = limit;
nvm->regions[i].offset = base;
nvm->regions[i].size = limit - base + 1;
/* No write access to descriptor; mask it out*/
nvm->regions[i].is_writable = idg_nvm_region_writable(access_map, id);
nvm->regions[i].is_readable = idg_nvm_region_readable(access_map, id);
dev_dbg(device, "Registered, %s id=%d offset=%lld size=%lld rd=%d wr=%d\n",
nvm->regions[i].name,
nvm->regions[i].id,
nvm->regions[i].offset,
nvm->regions[i].size,
nvm->regions[i].is_readable,
nvm->regions[i].is_writable);
if (nvm->regions[i].is_readable)
n++;
}
nvm->non_posted_erase = non_posted_erase;
dev_dbg(device, "Registered %d regions\n", n);
dev_dbg(device, "Non posted erase %d\n", nvm->non_posted_erase);
/* Need to add 1 to the amount of memory
* so it is reported as an even block
*/
nvm->size += 1;
return n;
}
static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
{
struct intel_dg_nvm *nvm = mtd->priv;
size_t total_len;
unsigned int idx;
ssize_t bytes;
loff_t from;
size_t len;
u8 region;
u64 addr;
if (WARN_ON(!nvm))
return -EINVAL;
if (!IS_ALIGNED(info->addr, SZ_4K) || !IS_ALIGNED(info->len, SZ_4K)) {
dev_err(&mtd->dev, "unaligned erase %llx %llx\n",
info->addr, info->len);
info->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
return -EINVAL;
}
total_len = info->len;
addr = info->addr;
guard(mutex)(&nvm->lock);
while (total_len > 0) {
if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(total_len, SZ_4K)) {
dev_err(&mtd->dev, "unaligned erase %llx %zx\n", addr, total_len);
info->fail_addr = addr;
return -ERANGE;
}
idx = idg_nvm_get_region(nvm, addr);
if (idx >= nvm->nregions) {
dev_err(&mtd->dev, "out of range");
info->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
return -ERANGE;
}
from = addr - nvm->regions[idx].offset;
region = nvm->regions[idx].id;
len = total_len;
if (len > nvm->regions[idx].size - from)
len = nvm->regions[idx].size - from;
dev_dbg(&mtd->dev, "erasing region[%d] %s from %llx len %zx\n",
region, nvm->regions[idx].name, from, len);
bytes = idg_erase(nvm, region, from, len, &info->fail_addr);
if (bytes < 0) {
dev_dbg(&mtd->dev, "erase failed with %zd\n", bytes);
info->fail_addr += nvm->regions[idx].offset;
return bytes;
}
addr += len;
total_len -= len;
}
return 0;
}
static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct intel_dg_nvm *nvm = mtd->priv;
unsigned int idx;
ssize_t ret;
u8 region;
if (WARN_ON(!nvm))
return -EINVAL;
idx = idg_nvm_get_region(nvm, from);
dev_dbg(&mtd->dev, "reading region[%d] %s from %lld len %zd\n",
nvm->regions[idx].id, nvm->regions[idx].name, from, len);
if (idx >= nvm->nregions) {
dev_err(&mtd->dev, "out of range");
return -ERANGE;
}
from -= nvm->regions[idx].offset;
region = nvm->regions[idx].id;
if (len > nvm->regions[idx].size - from)
len = nvm->regions[idx].size - from;
guard(mutex)(&nvm->lock);
ret = idg_read(nvm, region, from, len, buf);
if (ret < 0) {
dev_dbg(&mtd->dev, "read failed with %zd\n", ret);
return ret;
}
*retlen = ret;
return 0;
}
static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct intel_dg_nvm *nvm = mtd->priv;
unsigned int idx;
ssize_t ret;
u8 region;
if (WARN_ON(!nvm))
return -EINVAL;
idx = idg_nvm_get_region(nvm, to);
dev_dbg(&mtd->dev, "writing region[%d] %s to %lld len %zd\n",
nvm->regions[idx].id, nvm->regions[idx].name, to, len);
if (idx >= nvm->nregions) {
dev_err(&mtd->dev, "out of range");
return -ERANGE;
}
to -= nvm->regions[idx].offset;
region = nvm->regions[idx].id;
if (len > nvm->regions[idx].size - to)
len = nvm->regions[idx].size - to;
guard(mutex)(&nvm->lock);
ret = idg_write(nvm, region, to, len, buf);
if (ret < 0) {
dev_dbg(&mtd->dev, "write failed with %zd\n", ret);
return ret;
}
*retlen = ret;
return 0;
}
static void intel_dg_nvm_release(struct kref *kref)
{
struct intel_dg_nvm *nvm = container_of(kref, struct intel_dg_nvm, refcnt);
int i;
pr_debug("freeing intel_dg nvm\n");
for (i = 0; i < nvm->nregions; i++)
kfree(nvm->regions[i].name);
mutex_destroy(&nvm->lock);
kfree(nvm);
}
static int intel_dg_mtd_get_device(struct mtd_info *mtd)
{
struct mtd_info *master = mtd_get_master(mtd);
struct intel_dg_nvm *nvm = master->priv;
if (WARN_ON(!nvm))
return -EINVAL;
pr_debug("get mtd %s %d\n", mtd->name, kref_read(&nvm->refcnt));
kref_get(&nvm->refcnt);
return 0;
}
static void intel_dg_mtd_put_device(struct mtd_info *mtd)
{
struct mtd_info *master = mtd_get_master(mtd);
struct intel_dg_nvm *nvm = master->priv;
if (WARN_ON(!nvm))
return;
pr_debug("put mtd %s %d\n", mtd->name, kref_read(&nvm->refcnt));
kref_put(&nvm->refcnt, intel_dg_nvm_release);
}
static int intel_dg_nvm_init_mtd(struct intel_dg_nvm *nvm, struct device *device,
unsigned int nparts, bool writable_override)
{
struct mtd_partition *parts = NULL;
unsigned int i, n;
int ret;
dev_dbg(device, "registering with mtd\n");
nvm->mtd.owner = THIS_MODULE;
nvm->mtd.dev.parent = device;
nvm->mtd.flags = MTD_CAP_NORFLASH;
nvm->mtd.type = MTD_DATAFLASH;
nvm->mtd.priv = nvm;
nvm->mtd._write = intel_dg_mtd_write;
nvm->mtd._read = intel_dg_mtd_read;
nvm->mtd._erase = intel_dg_mtd_erase;
nvm->mtd._get_device = intel_dg_mtd_get_device;
nvm->mtd._put_device = intel_dg_mtd_put_device;
nvm->mtd.writesize = SZ_1; /* 1 byte granularity */
nvm->mtd.erasesize = SZ_4K; /* 4K bytes granularity */
nvm->mtd.size = nvm->size;
parts = kcalloc(nvm->nregions, sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
for (i = 0, n = 0; i < nvm->nregions && n < nparts; i++) {
if (!nvm->regions[i].is_readable)
continue;
parts[n].name = nvm->regions[i].name;
parts[n].offset = nvm->regions[i].offset;
parts[n].size = nvm->regions[i].size;
if (!nvm->regions[i].is_writable && !writable_override)
parts[n].mask_flags = MTD_WRITEABLE;
n++;
}
ret = mtd_device_register(&nvm->mtd, parts, n);
kfree(parts);
return ret;
}
static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
const struct auxiliary_device_id *aux_dev_id)
{
struct intel_dg_nvm_dev *invm = auxiliary_dev_to_intel_dg_nvm_dev(aux_dev);
struct intel_dg_nvm *nvm;
struct device *device;
unsigned int nregions;
unsigned int i, n;
int ret;
device = &aux_dev->dev;
/* count available regions */
for (nregions = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
if (invm->regions[i].name)
nregions++;
}
if (!nregions) {
dev_err(device, "no regions defined\n");
return -ENODEV;
}
nvm = kzalloc(struct_size(nvm, regions, nregions), GFP_KERNEL);
if (!nvm)
return -ENOMEM;
kref_init(&nvm->refcnt);
mutex_init(&nvm->lock);
for (n = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
if (!invm->regions[i].name)
continue;
char *name = kasprintf(GFP_KERNEL, "%s.%s",
dev_name(&aux_dev->dev), invm->regions[i].name);
if (!name)
continue;
nvm->regions[n].name = name;
nvm->regions[n].id = i;
n++;
}
nvm->nregions = n; /* in case where kasprintf fail */
nvm->base = devm_ioremap_resource(device, &invm->bar);
if (IS_ERR(nvm->base)) {
ret = PTR_ERR(nvm->base);
goto err;
}
if (invm->non_posted_erase) {
nvm->base2 = devm_ioremap_resource(device, &invm->bar2);
if (IS_ERR(nvm->base2)) {
ret = PTR_ERR(nvm->base2);
goto err;
}
}
ret = intel_dg_nvm_init(nvm, device, invm->non_posted_erase);
if (ret < 0) {
dev_err(device, "cannot initialize nvm %d\n", ret);
goto err;
}
ret = intel_dg_nvm_init_mtd(nvm, device, ret, invm->writable_override);
if (ret) {
dev_err(device, "failed init mtd %d\n", ret);
goto err;
}
dev_set_drvdata(&aux_dev->dev, nvm);
return 0;
err:
kref_put(&nvm->refcnt, intel_dg_nvm_release);
return ret;
}
static void intel_dg_mtd_remove(struct auxiliary_device *aux_dev)
{
struct intel_dg_nvm *nvm = dev_get_drvdata(&aux_dev->dev);
if (!nvm)
return;
mtd_device_unregister(&nvm->mtd);
dev_set_drvdata(&aux_dev->dev, NULL);
kref_put(&nvm->refcnt, intel_dg_nvm_release);
}
static const struct auxiliary_device_id intel_dg_mtd_id_table[] = {
{
.name = "i915.nvm",
},
{
.name = "xe.nvm",
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE(auxiliary, intel_dg_mtd_id_table);
static struct auxiliary_driver intel_dg_mtd_driver = {
.probe = intel_dg_mtd_probe,
.remove = intel_dg_mtd_remove,
.driver = {
/* auxiliary_driver_register() sets .name to be the modname */
},
.id_table = intel_dg_mtd_id_table
};
module_auxiliary_driver(intel_dg_mtd_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel DGFX MTD driver");
-96
View File
@@ -16,91 +16,9 @@ struct drm_gpusvm;
struct drm_gpusvm_notifier;
struct drm_gpusvm_ops;
struct drm_gpusvm_range;
struct drm_gpusvm_devmem;
struct drm_pagemap;
struct drm_pagemap_device_addr;
/**
* struct drm_gpusvm_devmem_ops - Operations structure for GPU SVM device memory
*
* This structure defines the operations for GPU Shared Virtual Memory (SVM)
* device memory. These operations are provided by the GPU driver to manage device memory
* allocations and perform operations such as migration between device memory and system
* RAM.
*/
struct drm_gpusvm_devmem_ops {
/**
* @devmem_release: Release device memory allocation (optional)
* @devmem_allocation: device memory allocation
*
* Release device memory allocation and drop a reference to device
* memory allocation.
*/
void (*devmem_release)(struct drm_gpusvm_devmem *devmem_allocation);
/**
* @populate_devmem_pfn: Populate device memory PFN (required for migration)
* @devmem_allocation: device memory allocation
* @npages: Number of pages to populate
* @pfn: Array of page frame numbers to populate
*
* Populate device memory page frame numbers (PFN).
*
* Return: 0 on success, a negative error code on failure.
*/
int (*populate_devmem_pfn)(struct drm_gpusvm_devmem *devmem_allocation,
unsigned long npages, unsigned long *pfn);
/**
* @copy_to_devmem: Copy to device memory (required for migration)
* @pages: Pointer to array of device memory pages (destination)
* @dma_addr: Pointer to array of DMA addresses (source)
* @npages: Number of pages to copy
*
* Copy pages to device memory.
*
* Return: 0 on success, a negative error code on failure.
*/
int (*copy_to_devmem)(struct page **pages,
dma_addr_t *dma_addr,
unsigned long npages);
/**
* @copy_to_ram: Copy to system RAM (required for migration)
* @pages: Pointer to array of device memory pages (source)
* @dma_addr: Pointer to array of DMA addresses (destination)
* @npages: Number of pages to copy
*
* Copy pages to system RAM.
*
* Return: 0 on success, a negative error code on failure.
*/
int (*copy_to_ram)(struct page **pages,
dma_addr_t *dma_addr,
unsigned long npages);
};
/**
* struct drm_gpusvm_devmem - Structure representing a GPU SVM device memory allocation
*
* @dev: Pointer to the device structure which device memory allocation belongs to
* @mm: Pointer to the mm_struct for the address space
* @detached: device memory allocations is detached from device pages
* @ops: Pointer to the operations structure for GPU SVM device memory
* @dpagemap: The struct drm_pagemap of the pages this allocation belongs to.
* @size: Size of device memory allocation
* @timeslice_expiration: Timeslice expiration in jiffies
*/
struct drm_gpusvm_devmem {
struct device *dev;
struct mm_struct *mm;
struct completion detached;
const struct drm_gpusvm_devmem_ops *ops;
struct drm_pagemap *dpagemap;
size_t size;
u64 timeslice_expiration;
};
/**
* struct drm_gpusvm_ops - Operations structure for GPU SVM
*
@@ -361,15 +279,6 @@ void drm_gpusvm_range_unmap_pages(struct drm_gpusvm *gpusvm,
struct drm_gpusvm_range *range,
const struct drm_gpusvm_ctx *ctx);
int drm_gpusvm_migrate_to_devmem(struct drm_gpusvm *gpusvm,
struct drm_gpusvm_range *range,
struct drm_gpusvm_devmem *devmem_allocation,
const struct drm_gpusvm_ctx *ctx);
int drm_gpusvm_evict_to_ram(struct drm_gpusvm_devmem *devmem_allocation);
const struct dev_pagemap_ops *drm_gpusvm_pagemap_ops_get(void);
bool drm_gpusvm_has_mapping(struct drm_gpusvm *gpusvm, unsigned long start,
unsigned long end);
@@ -380,11 +289,6 @@ drm_gpusvm_range_find(struct drm_gpusvm_notifier *notifier, unsigned long start,
void drm_gpusvm_range_set_unmapped(struct drm_gpusvm_range *range,
const struct mmu_notifier_range *mmu_range);
void drm_gpusvm_devmem_init(struct drm_gpusvm_devmem *devmem_allocation,
struct device *dev, struct mm_struct *mm,
const struct drm_gpusvm_devmem_ops *ops,
struct drm_pagemap *dpagemap, size_t size);
#ifdef CONFIG_LOCKDEP
/**
* drm_gpusvm_driver_set_lock() - Set the lock protecting accesses to GPU SVM
+135
View File
@@ -7,6 +7,7 @@
#include <linux/types.h>
struct drm_pagemap;
struct drm_pagemap_zdd;
struct device;
/**
@@ -91,6 +92,35 @@ struct drm_pagemap_ops {
struct device *dev,
struct drm_pagemap_device_addr addr);
/**
* @populate_mm: Populate part of the mm with @dpagemap memory,
* migrating existing data.
* @dpagemap: The struct drm_pagemap managing the memory.
* @start: The virtual start address in @mm
* @end: The virtual end address in @mm
* @mm: Pointer to a live mm. The caller must have an mmget()
* reference.
*
* The caller will have the mm lock at least in read mode.
* Note that there is no guarantee that the memory is resident
* after the function returns, it's best effort only.
* When the mm is not using the memory anymore,
* it will be released. The struct drm_pagemap might have a
* mechanism in place to reclaim the memory and the data will
* then be migrated. Typically to system memory.
* The implementation should hold sufficient runtime power-
* references while pages are used in an address space and
* should ideally guard against hardware device unbind in
* a way such that device pages are migrated back to system
* followed by device page removal. The implementation should
* return -ENODEV after device removal.
*
* Return: 0 if successful. Negative error code on error.
*/
int (*populate_mm)(struct drm_pagemap *dpagemap,
unsigned long start, unsigned long end,
struct mm_struct *mm,
unsigned long timeslice_ms);
};
/**
@@ -104,4 +134,109 @@ struct drm_pagemap {
struct device *dev;
};
struct drm_pagemap_devmem;
/**
* struct drm_pagemap_devmem_ops - Operations structure for GPU SVM device memory
*
* This structure defines the operations for GPU Shared Virtual Memory (SVM)
* device memory. These operations are provided by the GPU driver to manage device memory
* allocations and perform operations such as migration between device memory and system
* RAM.
*/
struct drm_pagemap_devmem_ops {
/**
* @devmem_release: Release device memory allocation (optional)
* @devmem_allocation: device memory allocation
*
* Release device memory allocation and drop a reference to device
* memory allocation.
*/
void (*devmem_release)(struct drm_pagemap_devmem *devmem_allocation);
/**
* @populate_devmem_pfn: Populate device memory PFN (required for migration)
* @devmem_allocation: device memory allocation
* @npages: Number of pages to populate
* @pfn: Array of page frame numbers to populate
*
* Populate device memory page frame numbers (PFN).
*
* Return: 0 on success, a negative error code on failure.
*/
int (*populate_devmem_pfn)(struct drm_pagemap_devmem *devmem_allocation,
unsigned long npages, unsigned long *pfn);
/**
* @copy_to_devmem: Copy to device memory (required for migration)
* @pages: Pointer to array of device memory pages (destination)
* @dma_addr: Pointer to array of DMA addresses (source)
* @npages: Number of pages to copy
*
* Copy pages to device memory.
*
* Return: 0 on success, a negative error code on failure.
*/
int (*copy_to_devmem)(struct page **pages,
dma_addr_t *dma_addr,
unsigned long npages);
/**
* @copy_to_ram: Copy to system RAM (required for migration)
* @pages: Pointer to array of device memory pages (source)
* @dma_addr: Pointer to array of DMA addresses (destination)
* @npages: Number of pages to copy
*
* Copy pages to system RAM.
*
* Return: 0 on success, a negative error code on failure.
*/
int (*copy_to_ram)(struct page **pages,
dma_addr_t *dma_addr,
unsigned long npages);
};
/**
* struct drm_pagemap_devmem - Structure representing a GPU SVM device memory allocation
*
* @dev: Pointer to the device structure which device memory allocation belongs to
* @mm: Pointer to the mm_struct for the address space
* @detached: device memory allocations is detached from device pages
* @ops: Pointer to the operations structure for GPU SVM device memory
* @dpagemap: The struct drm_pagemap of the pages this allocation belongs to.
* @size: Size of device memory allocation
* @timeslice_expiration: Timeslice expiration in jiffies
*/
struct drm_pagemap_devmem {
struct device *dev;
struct mm_struct *mm;
struct completion detached;
const struct drm_pagemap_devmem_ops *ops;
struct drm_pagemap *dpagemap;
size_t size;
u64 timeslice_expiration;
};
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
struct mm_struct *mm,
unsigned long start, unsigned long end,
unsigned long timeslice_ms,
void *pgmap_owner);
int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation);
const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void);
struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation,
struct device *dev, struct mm_struct *mm,
const struct drm_pagemap_devmem_ops *ops,
struct drm_pagemap *dpagemap, size_t size);
int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
unsigned long start, unsigned long end,
struct mm_struct *mm,
unsigned long timeslice_ms);
#endif
+4 -1
View File
@@ -846,6 +846,7 @@
/* BMG */
#define INTEL_BMG_IDS(MACRO__, ...) \
MACRO__(0xE202, ## __VA_ARGS__), \
MACRO__(0xE209, ## __VA_ARGS__), \
MACRO__(0xE20B, ## __VA_ARGS__), \
MACRO__(0xE20C, ## __VA_ARGS__), \
MACRO__(0xE20D, ## __VA_ARGS__), \
@@ -871,6 +872,8 @@
MACRO__(0xB08F, ## __VA_ARGS__), \
MACRO__(0xB090, ## __VA_ARGS__), \
MACRO__(0xB0A0, ## __VA_ARGS__), \
MACRO__(0xB0B0, ## __VA_ARGS__)
MACRO__(0xB0B0, ## __VA_ARGS__), \
MACRO__(0xFD80, ## __VA_ARGS__), \
MACRO__(0xFD81, ## __VA_ARGS__)
#endif /* __PCIIDS_H__ */
+32
View File
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright(c) 2019-2025, Intel Corporation. All rights reserved.
*/
#ifndef __INTEL_DG_NVM_AUX_H__
#define __INTEL_DG_NVM_AUX_H__
#include <linux/auxiliary_bus.h>
#include <linux/container_of.h>
#include <linux/ioport.h>
#include <linux/types.h>
#define INTEL_DG_NVM_REGIONS 13
struct intel_dg_nvm_region {
const char *name;
};
struct intel_dg_nvm_dev {
struct auxiliary_device aux_dev;
bool writable_override;
bool non_posted_erase;
struct resource bar;
struct resource bar2;
const struct intel_dg_nvm_region *regions;
};
#define auxiliary_dev_to_intel_dg_nvm_dev(auxiliary_dev) \
container_of(auxiliary_dev, struct intel_dg_nvm_dev, aux_dev)
#endif /* __INTEL_DG_NVM_AUX_H__ */
+4 -4
View File
@@ -925,9 +925,9 @@ struct drm_xe_gem_mmap_offset {
* - %DRM_XE_VM_CREATE_FLAG_LR_MODE - An LR, or Long Running VM accepts
* exec submissions to its exec_queues that don't have an upper time
* limit on the job execution time. But exec submissions to these
* don't allow any of the flags DRM_XE_SYNC_FLAG_SYNCOBJ,
* DRM_XE_SYNC_FLAG_TIMELINE_SYNCOBJ, DRM_XE_SYNC_FLAG_DMA_BUF,
* used as out-syncobjs, that is, together with DRM_XE_SYNC_FLAG_SIGNAL.
* don't allow any of the sync types DRM_XE_SYNC_TYPE_SYNCOBJ,
* DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ, used as out-syncobjs, that is,
* together with sync flag DRM_XE_SYNC_FLAG_SIGNAL.
* LR VMs can be created in recoverable page-fault mode using
* DRM_XE_VM_CREATE_FLAG_FAULT_MODE, if the device supports it.
* If that flag is omitted, the UMD can not rely on the slightly
@@ -1394,7 +1394,7 @@ struct drm_xe_sync {
/**
* @timeline_value: Input for the timeline sync object. Needs to be
* different than 0 when used with %DRM_XE_SYNC_FLAG_TIMELINE_SYNCOBJ.
* different than 0 when used with %DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ.
*/
__u64 timeline_value;