mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
Couple shrinker related fixes plus a series of patches fixing several
xe_mmio_gem issues around fault handler and destroy path. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEbSBwaO7dZQkcLOKj+mJfZA7rE8oFAmqr550ACgkQ+mJfZA7r E8p3UggAtIEI+BFbK7zeELK3zEtO85WQnIvUP/PprLu9Ga7Vvl+quXxZrWCHYcIE VwcK/5y2BGVKBmR1Vwm+PNBmtlbrNvIPGTn9u0oXB1bWVKqPT5o5MjY7lxdWhK95 MkLFo7rsQM791DAEUeo6IzbdHd6K9k2At8Yzz5+1zt97zxGmXSNpGRxV6Ee8/crU e/B1cQSfY8I4sjhAormTLZ13M6vRh1Yoy5P21UdCqIU/Eq/PjpkW2bcmM6+8K+qO 2a4y2CHIQnd+2bR4nEnuHYIa6DVuXEZIHh5v/8amenuZqBgVj7OcxUKiiZ4TnhMY bGe1UFyy0OWyEJ/X4ddGfCggpiFi6w== =0FwX -----END PGP SIGNATURE----- Merge tag 'drm-xe-fixes-2026-09-17' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-fixes Couple shrinker related fixes plus a series of patches fixing several xe_mmio_gem issues around fault handler and destroy path. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rodrigo Vivi <rodrigo.vivi@intel.com> Link: https://patch.msgid.link/aqvoKaPuLLPBGayA@intel.com
This commit is contained in:
commit
c24f824f0b
|
|
@ -318,8 +318,10 @@ void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold)
|
|||
static void xe_i2c_remove(void *data)
|
||||
{
|
||||
struct xe_i2c *i2c = data;
|
||||
struct xe_device *xe = tile_to_xe(i2c->mmio->tile);
|
||||
unsigned int i;
|
||||
|
||||
xe_i2c_irq_reset(xe);
|
||||
xe_amc_exit(i2c);
|
||||
|
||||
for (i = 0; i < XE_I2C_MAX_CLIENTS; i++) {
|
||||
|
|
@ -329,6 +331,7 @@ static void xe_i2c_remove(void *data)
|
|||
|
||||
bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
|
||||
xe_i2c_unregister_adapter(i2c);
|
||||
xe->i2c = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
#include "xe_mmio_gem.h"
|
||||
|
||||
#include <linux/dma-resv.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include "xe_device_types.h"
|
||||
|
||||
|
|
@ -37,12 +37,24 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *);
|
|||
struct xe_mmio_gem {
|
||||
struct drm_gem_object base;
|
||||
phys_addr_t phys_addr;
|
||||
struct page *dummy_page; /* protected by the GEM's dma_resv */
|
||||
bool destroyed; /* protected by the GEM's dma_resv */
|
||||
};
|
||||
|
||||
static int xe_mmio_gem_vm_may_split(struct vm_area_struct *area, unsigned long addr)
|
||||
{
|
||||
/*
|
||||
* Forbid splitting. Together with VM_DONTEXPAND, this keeps the VMA
|
||||
* matching the GEM object exactly.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct vm_ops = {
|
||||
.open = drm_gem_vm_open,
|
||||
.close = drm_gem_vm_close,
|
||||
.fault = xe_mmio_gem_vm_fault,
|
||||
.may_split = xe_mmio_gem_vm_may_split,
|
||||
};
|
||||
|
||||
static const struct drm_gem_object_funcs xe_mmio_gem_funcs = {
|
||||
|
|
@ -121,6 +133,8 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)
|
|||
{
|
||||
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
|
||||
|
||||
if (obj->dummy_page)
|
||||
__free_page(obj->dummy_page);
|
||||
drm_gem_object_release(base);
|
||||
kfree(obj);
|
||||
}
|
||||
|
|
@ -128,15 +142,31 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)
|
|||
/**
|
||||
* xe_mmio_gem_destroy - Destroy the GEM object that exposes an MMIO region
|
||||
* @gem: the GEM object to destroy
|
||||
* @file: DRM file descriptor previously passed to xe_mmio_gem_create()
|
||||
*
|
||||
* This function releases resources associated with the GEM object created by
|
||||
* xe_mmio_gem_create().
|
||||
*
|
||||
* See: "Exposing MMIO regions to userspace"
|
||||
*/
|
||||
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem)
|
||||
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file)
|
||||
{
|
||||
xe_mmio_gem_free(&gem->base);
|
||||
struct drm_gem_object *base = &gem->base;
|
||||
struct drm_device *dev = base->dev;
|
||||
|
||||
drm_vma_node_revoke(&base->vma_node, file);
|
||||
|
||||
dma_resv_lock(base->resv, NULL);
|
||||
gem->destroyed = true;
|
||||
dma_resv_unlock(base->resv);
|
||||
/*
|
||||
* Setting 'destroyed' under lock takes care of the subsequent faults.
|
||||
* Zap the existing PTEs to cut off access to the real MMIO through
|
||||
* currently mapped pages.
|
||||
*/
|
||||
drm_vma_node_unmap(&base->vma_node, dev->anon_inode->i_mapping);
|
||||
|
||||
drm_gem_object_put(base);
|
||||
}
|
||||
|
||||
static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *vma)
|
||||
|
|
@ -147,8 +177,6 @@ static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *
|
|||
if ((vma->vm_flags & VM_SHARED) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set vm_pgoff (used as a fake buffer offset by DRM) to 0 */
|
||||
vma->vm_pgoff = 0;
|
||||
vma->vm_page_prot = pgprot_noncached(vma_get_page_prot(vma));
|
||||
vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
|
||||
VM_DONTCOPY | VM_NORESERVE);
|
||||
|
|
@ -157,51 +185,47 @@ static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void xe_mmio_gem_release_dummy_page(struct drm_device *dev, void *res)
|
||||
static int alloc_dummy_page_if_needed(struct drm_gem_object *base)
|
||||
{
|
||||
__free_page((struct page *)res);
|
||||
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
|
||||
|
||||
dma_resv_assert_held(base->resv);
|
||||
if (!obj->dummy_page)
|
||||
obj->dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
||||
|
||||
return obj->dummy_page ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_area_struct *vma)
|
||||
static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct drm_gem_object *base = vma->vm_private_data;
|
||||
struct drm_device *dev = base->dev;
|
||||
vm_fault_t ret = VM_FAULT_NOPAGE;
|
||||
struct page *page;
|
||||
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
|
||||
unsigned long pfn;
|
||||
unsigned long i;
|
||||
|
||||
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
||||
if (!page)
|
||||
if (alloc_dummy_page_if_needed(base))
|
||||
return VM_FAULT_OOM;
|
||||
|
||||
if (drmm_add_action_or_reset(dev, xe_mmio_gem_release_dummy_page, page))
|
||||
return VM_FAULT_OOM;
|
||||
pfn = page_to_pfn(obj->dummy_page);
|
||||
|
||||
pfn = page_to_pfn(page);
|
||||
|
||||
/* Map the entire VMA to the same dummy page */
|
||||
for (i = 0; i < base->size; i += PAGE_SIZE) {
|
||||
unsigned long addr = vma->vm_start + i;
|
||||
|
||||
ret = vmf_insert_pfn(vma, addr, pfn);
|
||||
if (ret & VM_FAULT_ERROR)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return vmf_insert_pfn_prot(vma, vmf->address, pfn,
|
||||
vm_get_page_prot(vma->vm_flags));
|
||||
}
|
||||
|
||||
static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
|
||||
static vm_fault_t xe_mmio_gem_vm_fault_locked(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct drm_gem_object *base = vma->vm_private_data;
|
||||
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
|
||||
struct drm_device *dev = base->dev;
|
||||
vm_fault_t ret = VM_FAULT_NOPAGE;
|
||||
unsigned long i;
|
||||
unsigned long addr, pfn;
|
||||
int idx;
|
||||
|
||||
dma_resv_assert_held(base->resv);
|
||||
if (obj->destroyed)
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
if (!drm_dev_enter(dev, &idx)) {
|
||||
/*
|
||||
* Provide a dummy page to avoid SIGBUS for events such as hot-unplug.
|
||||
|
|
@ -209,18 +233,30 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
|
|||
* It is assumed the userspace will receive the notification via some
|
||||
* other channel (e.g. drm uevent).
|
||||
*/
|
||||
return xe_mmio_gem_vm_fault_dummy_page(vma);
|
||||
return xe_mmio_gem_vm_fault_dummy_page(vmf);
|
||||
}
|
||||
|
||||
for (i = 0; i < base->size; i += PAGE_SIZE) {
|
||||
unsigned long addr = vma->vm_start + i;
|
||||
unsigned long phys_addr = obj->phys_addr + i;
|
||||
|
||||
ret = vmf_insert_pfn(vma, addr, PHYS_PFN(phys_addr));
|
||||
pfn = PHYS_PFN(obj->phys_addr);
|
||||
for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
|
||||
ret = vmf_insert_pfn(vma, addr, pfn);
|
||||
if (ret & VM_FAULT_ERROR)
|
||||
break;
|
||||
|
||||
pfn++;
|
||||
}
|
||||
|
||||
drm_dev_exit(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct drm_gem_object *base = vma->vm_private_data;
|
||||
vm_fault_t ret;
|
||||
|
||||
dma_resv_lock(base->resv, NULL);
|
||||
ret = xe_mmio_gem_vm_fault_locked(vmf);
|
||||
dma_resv_unlock(base->resv);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ struct xe_mmio_gem;
|
|||
struct xe_mmio_gem *xe_mmio_gem_create(struct xe_device *xe, struct drm_file *file,
|
||||
phys_addr_t phys_addr, size_t size);
|
||||
u64 xe_mmio_gem_mmap_offset(struct xe_mmio_gem *gem);
|
||||
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem);
|
||||
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file);
|
||||
|
||||
#endif /* _XE_MMIO_GEM_H_ */
|
||||
|
|
|
|||
|
|
@ -54,13 +54,40 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea
|
|||
write_unlock(&shrinker->lock);
|
||||
}
|
||||
|
||||
static s64 __xe_shrinker_walk(struct xe_device *xe,
|
||||
static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker)
|
||||
{
|
||||
struct xe_device *xe = shrinker->xe;
|
||||
|
||||
if (xe_pm_runtime_get_if_active(xe))
|
||||
return true;
|
||||
|
||||
if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
|
||||
xe_pm_runtime_get(xe);
|
||||
return true;
|
||||
}
|
||||
|
||||
queue_work(xe->unordered_wq, &shrinker->pm_worker);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
|
||||
{
|
||||
if (runtime_pm)
|
||||
xe_pm_runtime_put(shrinker->xe);
|
||||
}
|
||||
|
||||
static int __xe_shrinker_walk(struct xe_shrinker *shrinker,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
const struct xe_bo_shrink_flags flags,
|
||||
unsigned long to_scan, unsigned long *scanned)
|
||||
unsigned long to_scan, unsigned long *scanned,
|
||||
unsigned long *freed)
|
||||
{
|
||||
struct xe_device *xe = shrinker->xe;
|
||||
unsigned int mem_type;
|
||||
s64 freed = 0, lret;
|
||||
bool rpm = false;
|
||||
int ret = 0;
|
||||
s64 lret;
|
||||
|
||||
for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) {
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type);
|
||||
|
|
@ -74,23 +101,35 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
|
|||
if (!man || !man->use_tt)
|
||||
continue;
|
||||
|
||||
if (mem_type != XE_PL_SYSTEM && !rpm &&
|
||||
xe_device_is_l2_flush_optimized(xe)) {
|
||||
if (!__xe_shrinker_runtime_pm_get(shrinker))
|
||||
break;
|
||||
rpm = true;
|
||||
}
|
||||
|
||||
ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) {
|
||||
if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
|
||||
continue;
|
||||
|
||||
lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned);
|
||||
if (lret < 0)
|
||||
return lret;
|
||||
if (lret < 0) {
|
||||
ret = lret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
freed += lret;
|
||||
*freed += lret;
|
||||
if (*scanned >= to_scan)
|
||||
break;
|
||||
goto out;
|
||||
}
|
||||
/* Trylocks should never error, just fail. */
|
||||
xe_assert(xe, !IS_ERR(ttm_bo));
|
||||
}
|
||||
|
||||
return freed;
|
||||
out:
|
||||
xe_shrinker_runtime_pm_put(shrinker, rpm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -99,40 +138,36 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
|
|||
* add writeback. This avoids stalls and explicit writebacks with light or
|
||||
* moderate memory pressure.
|
||||
*/
|
||||
static s64 xe_shrinker_walk(struct xe_device *xe,
|
||||
static int xe_shrinker_walk(struct xe_shrinker *shrinker,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
const struct xe_bo_shrink_flags flags,
|
||||
unsigned long to_scan, unsigned long *scanned)
|
||||
unsigned long to_scan, unsigned long *scanned,
|
||||
unsigned long *freed)
|
||||
{
|
||||
bool no_wait_gpu = true;
|
||||
struct xe_bo_shrink_flags save_flags = flags;
|
||||
s64 lret, freed;
|
||||
int ret;
|
||||
|
||||
swap(no_wait_gpu, ctx->no_wait_gpu);
|
||||
save_flags.writeback = false;
|
||||
lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
|
||||
ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
|
||||
freed);
|
||||
swap(no_wait_gpu, ctx->no_wait_gpu);
|
||||
if (lret < 0 || *scanned >= to_scan)
|
||||
return lret;
|
||||
if (ret || *scanned >= to_scan)
|
||||
return ret;
|
||||
|
||||
freed = lret;
|
||||
if (!ctx->no_wait_gpu) {
|
||||
lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
|
||||
if (lret < 0)
|
||||
return lret;
|
||||
freed += lret;
|
||||
if (*scanned >= to_scan)
|
||||
return freed;
|
||||
ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
|
||||
freed);
|
||||
if (ret || *scanned >= to_scan)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (flags.writeback) {
|
||||
lret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned);
|
||||
if (lret < 0)
|
||||
return lret;
|
||||
freed += lret;
|
||||
}
|
||||
if (flags.writeback)
|
||||
ret = __xe_shrinker_walk(shrinker, ctx, flags, to_scan, scanned,
|
||||
freed);
|
||||
|
||||
return freed;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
|
|
@ -180,22 +215,7 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!xe_pm_runtime_get_if_active(xe)) {
|
||||
if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
|
||||
xe_pm_runtime_get(xe);
|
||||
return true;
|
||||
}
|
||||
queue_work(xe->unordered_wq, &shrinker->pm_worker);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
|
||||
{
|
||||
if (runtime_pm)
|
||||
xe_pm_runtime_put(shrinker->xe);
|
||||
return __xe_shrinker_runtime_pm_get(shrinker);
|
||||
}
|
||||
|
||||
static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
|
||||
|
|
@ -214,7 +234,6 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
|
|||
bool runtime_pm;
|
||||
bool purgeable;
|
||||
bool can_backup = !!(sc->gfp_mask & __GFP_FS);
|
||||
s64 lret;
|
||||
|
||||
nr_to_scan = sc->nr_to_scan;
|
||||
|
||||
|
|
@ -225,12 +244,9 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
|
|||
/* Might need runtime PM. Try to wake early if it looks like it. */
|
||||
runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup);
|
||||
|
||||
if (purgeable && nr_scanned < nr_to_scan) {
|
||||
lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
|
||||
nr_to_scan, &nr_scanned);
|
||||
if (lret >= 0)
|
||||
freed += lret;
|
||||
}
|
||||
if (purgeable && nr_scanned < nr_to_scan)
|
||||
xe_shrinker_walk(shrinker, &ctx, shrink_flags,
|
||||
nr_to_scan, &nr_scanned, &freed);
|
||||
|
||||
sc->nr_scanned = nr_scanned;
|
||||
if (nr_scanned >= nr_to_scan || !can_backup)
|
||||
|
|
@ -242,10 +258,8 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
|
|||
|
||||
shrink_flags.purge = false;
|
||||
|
||||
lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
|
||||
nr_to_scan, &nr_scanned);
|
||||
if (lret >= 0)
|
||||
freed += lret;
|
||||
xe_shrinker_walk(shrinker, &ctx, shrink_flags,
|
||||
nr_to_scan, &nr_scanned, &freed);
|
||||
|
||||
sc->nr_scanned = nr_scanned;
|
||||
out:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user