diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h --- a/drivers/gpu/drm/xe/xe_defaults.h +++ b/drivers/gpu/drm/xe/xe_defaults.h @@ -24,5 +24,6 @@ #define XE_DEFAULT_SVM_NOTIFIER_SIZE 512 #define XE_DEFAULT_SHRINK_ACTIVE_MS 1000 #define XE_DEFAULT_SHRINK_DIRECT_BACKUP false +#define XE_DEFAULT_HIBERNATE_WRITEBACK false #endif diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c --- a/drivers/gpu/drm/xe/xe_module.c +++ b/drivers/gpu/drm/xe/xe_module.c @@ -32,6 +32,7 @@ struct xe_modparam xe_modparam = { .svm_notifier_size = XE_DEFAULT_SVM_NOTIFIER_SIZE, .shrink_active_ms = XE_DEFAULT_SHRINK_ACTIVE_MS, .shrink_direct_backup = XE_DEFAULT_SHRINK_DIRECT_BACKUP, + .hibernate_writeback = XE_DEFAULT_HIBERNATE_WRITEBACK, /* the rest are 0 by default */ }; @@ -48,6 +49,10 @@ module_param_named(shrink_direct_backup, xe_modparam.shrink_direct_backup, bool, MODULE_PARM_DESC(shrink_direct_backup, "Back up buffer objects in direct reclaim too, not only in kswapd " "[default=" __stringify(XE_DEFAULT_SHRINK_DIRECT_BACKUP) "]"); +module_param_named(hibernate_writeback, xe_modparam.hibernate_writeback, bool, 0600); +MODULE_PARM_DESC(hibernate_writeback, "Write buffer objects to swap before hibernation, " + "integrated GPUs only [default=" __stringify(XE_DEFAULT_HIBERNATE_WRITEBACK) "]"); + module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h --- a/drivers/gpu/drm/xe/xe_module.h +++ b/drivers/gpu/drm/xe/xe_module.h @@ -27,6 +27,7 @@ struct xe_modparam { u32 svm_notifier_size; unsigned int shrink_active_ms; bool shrink_direct_backup; + bool hibernate_writeback; }; extern struct xe_modparam xe_modparam; diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c --- a/drivers/gpu/drm/xe/xe_pm.c +++ b/drivers/gpu/drm/xe/xe_pm.c @@ -26,6 +26,7 @@ #include "xe_pcode.h" #include "xe_printk.h" #include "xe_pxp.h" +#include "xe_shrinker.h" #include "xe_sriov_vf_ccs.h" #include "xe_sysctrl.h" #include "xe_trace.h" @@ -450,6 +451,9 @@ static int xe_pm_notifier_callback(struct notifier_block *nb, if (err) drm_dbg(&xe->drm, "Notifier evict user failed (%d)\n", err); + if (action == PM_HIBERNATION_PREPARE) + xe_shrinker_backup_for_hibernation(xe->mem.shrinker); + err = xe_bo_notifier_prepare_all_pinned(xe); if (err) drm_dbg(&xe->drm, "Notifier prepare pin failed (%d)\n", err); diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c --- a/drivers/gpu/drm/xe/xe_shrinker.c +++ b/drivers/gpu/drm/xe/xe_shrinker.c @@ -172,6 +172,34 @@ static int xe_shrinker_walk(struct xe_shrinker *shrinker, return ret; } +void xe_shrinker_backup_for_hibernation(struct xe_shrinker *shrinker) +{ + struct ttm_operation_ctx ctx = { + .interruptible = false, + .no_wait_gpu = false, + }; + struct xe_bo_shrink_flags flags = { + .purge = false, + .writeback = true, + }; + unsigned long to_scan, scanned = 0, freed = 0; + ktime_t start = ktime_get(); + int ret; + + if (!READ_ONCE(xe_modparam.hibernate_writeback) || IS_DGFX(shrinker->xe) || + !ttm_backup_bytes_avail()) + return; + + read_lock(&shrinker->lock); + to_scan = shrinker->shrinkable_pages; + read_unlock(&shrinker->lock); + + ret = __xe_shrinker_walk(shrinker, &ctx, flags, to_scan, &scanned, &freed); + drm_info(&shrinker->xe->drm, + "Hibernation writeback: %lu of %lu pages scanned, %lu freed in %lld ms (%d)\n", + scanned, to_scan, freed, ktime_ms_delta(ktime_get(), start), ret); +} + static inline bool xe_shrinker_can_backup(struct xe_shrinker *shrinker, const struct shrink_control *sc) { diff --git a/drivers/gpu/drm/xe/xe_shrinker.h b/drivers/gpu/drm/xe/xe_shrinker.h --- a/drivers/gpu/drm/xe/xe_shrinker.h +++ b/drivers/gpu/drm/xe/xe_shrinker.h @@ -13,4 +13,6 @@ void xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long p int xe_shrinker_create(struct xe_device *xe); +void xe_shrinker_backup_for_hibernation(struct xe_shrinker *shrinker); + #endif