diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3739-drm-ttm-ttm-code-modifications-to-fix-the.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3739-drm-ttm-ttm-code-modifications-to-fix-the.patch | 3640 |
1 files changed, 3640 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3739-drm-ttm-ttm-code-modifications-to-fix-the.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3739-drm-ttm-ttm-code-modifications-to-fix-the.patch new file mode 100644 index 00000000..c3606598 --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/3739-drm-ttm-ttm-code-modifications-to-fix-the.patch @@ -0,0 +1,3640 @@ +From 17f5622d02c6a20b55238692f556aa4caf5b63a9 Mon Sep 17 00:00:00 2001 +From: Kalyan Alle <kalyan.alle@amd.com> +Date: Mon, 23 Jul 2018 14:28:55 +0530 +Subject: [PATCH 3739/4131] drm/ttm: ttm code modifications to fix the + +build/compilation issues + +Signed-off-by: Kalyan Alle <kalyan.alle@amd.com> +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 7 +- + drivers/gpu/drm/ast/ast_ttm.c | 1 - + drivers/gpu/drm/cirrus/cirrus_ttm.c | 1 - + drivers/gpu/drm/mgag200/mgag200_ttm.c | 1 - + drivers/gpu/drm/nouveau/nouveau_bo.c | 10 +- + drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- + drivers/gpu/drm/qxl/qxl_release.c | 4 +- + drivers/gpu/drm/qxl/qxl_ttm.c | 4 +- + drivers/gpu/drm/radeon/radeon_ttm.c | 8 +- + drivers/gpu/drm/ttm/Makefile | 19 +- + drivers/gpu/drm/ttm/ttm_bo.c | 294 +++++++---------- + drivers/gpu/drm/ttm/ttm_bo_util.c | 52 ++- + drivers/gpu/drm/ttm/ttm_bo_vm.c | 61 ++-- + drivers/gpu/drm/ttm/ttm_execbuf_util.c | 28 +- + drivers/gpu/drm/ttm/ttm_memory.c | 143 +++++++-- + drivers/gpu/drm/ttm/ttm_module.c | 4 +- + drivers/gpu/drm/ttm/ttm_page_alloc.c | 531 ++++++++++++++++++++++--------- + drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 359 ++++++++++++++------- + drivers/gpu/drm/ttm/ttm_tt.c | 68 +++- + drivers/gpu/drm/virtio/virtgpu_ttm.c | 1 - + drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 1 - + include/drm/ttm/ttm_bo_driver.h | 87 ++--- + include/drm/ttm/ttm_memory.h | 69 +--- + include/drm/ttm/ttm_page_alloc.h | 7 +- + include/linux/reservation.h | 22 ++ + 25 files changed, 1100 insertions(+), 684 deletions(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +index 548239c..2c4eed2 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +@@ -537,7 +537,7 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict, + if (unlikely(r)) { + goto out_cleanup; + } +- r = ttm_bo_move_ttm(bo, ctx->interruptible, ctx->no_wait_gpu, new_mem); ++ r = ttm_bo_move_ttm(bo, ctx, new_mem); + out_cleanup: + ttm_bo_mem_put(bo, &tmp_mem); + return r; +@@ -568,7 +568,7 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict, + if (unlikely(r)) { + return r; + } +- r = ttm_bo_move_ttm(bo, ctx->interruptible, ctx->no_wait_gpu, &tmp_mem); ++ r = ttm_bo_move_ttm(bo, ctx, &tmp_mem); + if (unlikely(r)) { + goto out_cleanup; + } +@@ -630,8 +630,7 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, + + if (r) { + memcpy: +- r = ttm_bo_move_memcpy(bo, ctx->interruptible, +- ctx->no_wait_gpu, new_mem); ++ r = ttm_bo_move_memcpy(bo, ctx, new_mem); + if (r) { + return r; + } +diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c +index 10c36c9..846e4d8 100644 +--- a/drivers/gpu/drm/ast/ast_ttm.c ++++ b/drivers/gpu/drm/ast/ast_ttm.c +@@ -237,7 +237,6 @@ struct ttm_bo_driver ast_bo_driver = { + .verify_access = ast_bo_verify_access, + .io_mem_reserve = &ast_ttm_io_mem_reserve, + .io_mem_free = &ast_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + }; + + int ast_mm_init(struct ast_private *ast) +diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c +index b40e294..4a9c232 100644 +--- a/drivers/gpu/drm/cirrus/cirrus_ttm.c ++++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c +@@ -237,7 +237,6 @@ struct ttm_bo_driver cirrus_bo_driver = { + .verify_access = cirrus_bo_verify_access, + .io_mem_reserve = &cirrus_ttm_io_mem_reserve, + .io_mem_free = &cirrus_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + }; + + int cirrus_mm_init(struct cirrus_device *cirrus) +diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c +index 85e39c3..882e902 100644 +--- a/drivers/gpu/drm/mgag200/mgag200_ttm.c ++++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c +@@ -237,7 +237,6 @@ struct ttm_bo_driver mgag200_bo_driver = { + .verify_access = mgag200_bo_verify_access, + .io_mem_reserve = &mgag200_ttm_io_mem_reserve, + .io_mem_free = &mgag200_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + }; + + int mgag200_mm_init(struct mga_device *mdev) +diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c +index d25ccd4..47827a4 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_bo.c ++++ b/drivers/gpu/drm/nouveau/nouveau_bo.c +@@ -1154,7 +1154,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, + if (ret) + goto out; + +- ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, new_reg); ++ ret = ttm_bo_move_ttm(bo, &ctx, new_reg); + out: + ttm_bo_mem_put(bo, &tmp_reg); + return ret; +@@ -1183,7 +1183,7 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, + if (ret) + return ret; + +- ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, &tmp_reg); ++ ret = ttm_bo_move_ttm(bo, &ctx, &tmp_reg); + if (ret) + goto out; + +@@ -1307,8 +1307,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, + /* Fallback to software copy. */ + ret = ttm_bo_wait(bo, ctx->interruptible, ctx->no_wait_gpu); + if (ret == 0) +- ret = ttm_bo_move_memcpy(bo, ctx->interruptible, +- ctx->no_wait_gpu, new_reg); ++ ret = ttm_bo_move_memcpy(bo, ctx, new_reg); + + out: + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { +@@ -1446,7 +1445,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) + } + + static int +-nouveau_ttm_tt_populate(struct ttm_tt *ttm) ++nouveau_ttm_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx) + { + struct ttm_dma_tt *ttm_dma = (void *)ttm; + struct nouveau_drm *drm; +@@ -1579,7 +1578,6 @@ struct ttm_bo_driver nouveau_bo_driver = { + .fault_reserve_notify = &nouveau_ttm_fault_reserve_notify, + .io_mem_reserve = &nouveau_ttm_io_mem_reserve, + .io_mem_free = &nouveau_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + }; + + struct nvkm_vma * +diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c +index 60ffb70..75ec8b1 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_gem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c +@@ -349,7 +349,7 @@ validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence, + + list_del(&nvbo->entry); + nvbo->reserved_by = NULL; +- ttm_bo_unreserve_ticket(&nvbo->bo, &op->ticket); ++ ttm_bo_unreserve(&nvbo->bo); + drm_gem_object_unreference_unlocked(&nvbo->gem); + } + } +diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c +index 1f90d0e..c010d9c 100644 +--- a/drivers/gpu/drm/qxl/qxl_release.c ++++ b/drivers/gpu/drm/qxl/qxl_release.c +@@ -459,7 +459,7 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release) + trace_dma_fence_emit(&release->base); + + driver = bdev->driver; +- glob = bo->glob; ++ glob = bdev->glob; + + spin_lock(&glob->lru_lock); + +@@ -469,7 +469,7 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release) + + reservation_object_add_shared_fence(bo->resv, &release->base); + ttm_bo_add_to_lru(bo); +- __ttm_bo_unreserve(bo); ++ reservation_object_unlock(bo->resv); + } + spin_unlock(&glob->lru_lock); + ww_acquire_fini(&release->ticket); +diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c +index a283352..0944f7c 100644 +--- a/drivers/gpu/drm/qxl/qxl_ttm.c ++++ b/drivers/gpu/drm/qxl/qxl_ttm.c +@@ -359,8 +359,7 @@ static int qxl_bo_move(struct ttm_buffer_object *bo, bool evict, + qxl_move_null(bo, new_mem); + return 0; + } +- return ttm_bo_move_memcpy(bo, ctx->interruptible, ctx->no_wait_gpu, +- new_mem); ++ return ttm_bo_move_memcpy(bo, ctx, new_mem); + } + + static void qxl_bo_move_notify(struct ttm_buffer_object *bo, +@@ -391,7 +390,6 @@ static struct ttm_bo_driver qxl_bo_driver = { + .verify_access = &qxl_verify_access, + .io_mem_reserve = &qxl_ttm_io_mem_reserve, + .io_mem_free = &qxl_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + .move_notify = &qxl_bo_move_notify, + }; + +diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c +index e651589..801249d 100644 +--- a/drivers/gpu/drm/radeon/radeon_ttm.c ++++ b/drivers/gpu/drm/radeon/radeon_ttm.c +@@ -347,7 +347,7 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo, + if (unlikely(r)) { + goto out_cleanup; + } +- r = ttm_bo_move_ttm(bo, interruptible, no_wait_gpu, new_mem); ++ r = ttm_bo_move_ttm(bo, &ctx, new_mem); + out_cleanup: + ttm_bo_mem_put(bo, &tmp_mem); + return r; +@@ -380,7 +380,7 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo, + if (unlikely(r)) { + return r; + } +- r = ttm_bo_move_ttm(bo, interruptible, no_wait_gpu, &tmp_mem); ++ r = ttm_bo_move_ttm(bo, &ctx, &tmp_mem); + if (unlikely(r)) { + goto out_cleanup; + } +@@ -445,8 +445,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo, bool evict, + + if (r) { + memcpy: +- r = ttm_bo_move_memcpy(bo, ctx->interruptible, +- ctx->no_wait_gpu, new_mem); ++ r = ttm_bo_move_memcpy(bo, ctx, new_mem); + if (r) { + return r; + } +@@ -845,7 +844,6 @@ static struct ttm_bo_driver radeon_bo_driver = { + .fault_reserve_notify = &radeon_bo_fault_reserve_notify, + .io_mem_reserve = &radeon_ttm_io_mem_reserve, + .io_mem_free = &radeon_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + }; + + int radeon_ttm_init(struct radeon_device *rdev) +diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile +index 7c57dac..e2c3ed2 100644 +--- a/drivers/gpu/drm/ttm/Makefile ++++ b/drivers/gpu/drm/ttm/Makefile +@@ -2,11 +2,16 @@ + # + # Makefile for the drm device driver. This driver provides support for the + +-ccflags-y := -I$(src)/. +-ttm-y := ttm_memory.o ttm_tt.o ttm_bo.o \ +- ttm_bo_util.o ttm_bo_vm.o ttm_module.o \ +- ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \ +- ttm_bo_manager.o ttm_page_alloc_dma.o ttm_debug.o ttm_tracepoints.o +-ttm-$(CONFIG_AGP) += ttm_agp_backend.o ++ifneq (,$(BUILD_AS_DKMS)) ++ TTM_NAME = amdttm ++else ++ TTM_NAME = ttm ++endif + +-obj-$(CONFIG_DRM_TTM) += ttm.o ++$(TTM_NAME)-y := ttm_memory.o ttm_tt.o ttm_bo.o \ ++ ttm_bo_util.o ttm_bo_vm.o ttm_module.o \ ++ ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \ ++ ttm_bo_manager.o ttm_page_alloc_dma.o ++$(TTM_NAME)-$(CONFIG_AGP) += ttm_agp_backend.o ++ ++obj-$(CONFIG_DRM_TTM) += $(TTM_NAME).o +diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c +index cf3aef0..7f0f16b 100644 +--- a/drivers/gpu/drm/ttm/ttm_bo.c ++++ b/drivers/gpu/drm/ttm/ttm_bo.c +@@ -28,6 +28,7 @@ + * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> + */ + ++#undef pr_fmt + #define pr_fmt(fmt) "[TTM] " fmt + + #include <drm/ttm/ttm_module.h> +@@ -42,11 +43,6 @@ + #include <linux/atomic.h> + #include <linux/reservation.h> + +-#define TTM_ASSERT_LOCKED(param) +-#define TTM_DEBUG(fmt, arg...) +-#define TTM_BO_HASH_ORDER 13 +- +-static int ttm_bo_swapout(struct ttm_mem_shrink *shrink); + static void ttm_bo_global_kobj_release(struct kobject *kobj); + + static struct attribute ttm_bo_count = { +@@ -54,6 +50,12 @@ static struct attribute ttm_bo_count = { + .mode = S_IRUGO + }; + ++/* default destructor */ ++static void ttm_bo_default_destroy(struct ttm_buffer_object *bo) ++{ ++ kfree(bo); ++} ++ + static inline int ttm_mem_type_from_place(const struct ttm_place *place, + uint32_t *mem_type) + { +@@ -148,15 +150,11 @@ static void ttm_bo_release_list(struct kref *list_kref) + BUG_ON(!list_empty(&bo->lru)); + BUG_ON(!list_empty(&bo->ddestroy)); + ttm_tt_destroy(bo->ttm); +- atomic_dec(&bo->glob->bo_count); ++ atomic_dec(&bo->bdev->glob->bo_count); + dma_fence_put(bo->moving); + reservation_object_fini(&bo->ttm_resv); + mutex_destroy(&bo->wu_mutex); +- if (bo->destroy) +- bo->destroy(bo); +- else { +- kfree(bo); +- } ++ bo->destroy(bo); + ttm_mem_global_free(bdev->glob->mem_glob, acc_size); + } + +@@ -165,7 +163,7 @@ void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) + struct ttm_bo_device *bdev = bo->bdev; + struct ttm_mem_type_manager *man; + +- lockdep_assert_held(&bo->resv->lock.base); ++ reservation_object_assert_held(bo->resv); + + if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { + +@@ -175,10 +173,10 @@ void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) + list_add_tail(&bo->lru, &man->lru[bo->priority]); + kref_get(&bo->list_kref); + +- if (bo->ttm && !(bo->ttm->page_flags & +- (TTM_PAGE_FLAG_SG | TTM_PAGE_FLAG_SWAPPED))) { ++ if (bo->ttm && !(bo->ttm->page_flags & ++ (TTM_PAGE_FLAG_SG | TTM_PAGE_FLAG_SWAPPED))) { + list_add_tail(&bo->swap, +- &bo->glob->swap_lru[bo->priority]); ++ &bdev->glob->swap_lru[bo->priority]); + kref_get(&bo->list_kref); + } + } +@@ -209,63 +207,23 @@ void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) + + void ttm_bo_del_sub_from_lru(struct ttm_buffer_object *bo) + { +- spin_lock(&bo->glob->lru_lock); ++ struct ttm_bo_global *glob = bo->bdev->glob; ++ ++ spin_lock(&glob->lru_lock); + ttm_bo_del_from_lru(bo); +- spin_unlock(&bo->glob->lru_lock); ++ spin_unlock(&glob->lru_lock); + } + EXPORT_SYMBOL(ttm_bo_del_sub_from_lru); + + void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo) + { +- lockdep_assert_held(&bo->resv->lock.base); ++ reservation_object_assert_held(bo->resv); + + ttm_bo_del_from_lru(bo); + ttm_bo_add_to_lru(bo); + } + EXPORT_SYMBOL(ttm_bo_move_to_lru_tail); + +-/* +- * Call bo->mutex locked. +- */ +-static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) +-{ +- struct ttm_bo_device *bdev = bo->bdev; +- int ret = 0; +- uint32_t page_flags = 0; +- +- TTM_ASSERT_LOCKED(&bo->mutex); +- bo->ttm = NULL; +- +- if (bdev->need_dma32) +- page_flags |= TTM_PAGE_FLAG_DMA32; +- +- switch (bo->type) { +- case ttm_bo_type_device: +- if (zero_alloc) +- page_flags |= TTM_PAGE_FLAG_ZERO_ALLOC; +- case ttm_bo_type_kernel: +- bo->ttm = bdev->driver->ttm_tt_create(bo, page_flags); +- if (unlikely(bo->ttm == NULL)) +- ret = -ENOMEM; +- break; +- case ttm_bo_type_sg: +- bo->ttm = bdev->driver->ttm_tt_create(bo, +- page_flags | TTM_PAGE_FLAG_SG); +- if (unlikely(bo->ttm == NULL)) { +- ret = -ENOMEM; +- break; +- } +- bo->ttm->sg = bo->sg; +- break; +- default: +- pr_err("Illegal buffer object type\n"); +- ret = -EINVAL; +- break; +- } +- +- return ret; +-} +- + static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem, bool evict, + struct ttm_operation_ctx *ctx) +@@ -293,7 +251,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, + if (!(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) { + if (bo->ttm == NULL) { + bool zero = !(old_man->flags & TTM_MEMTYPE_FLAG_FIXED); +- ret = ttm_bo_add_ttm(bo, zero); ++ ret = ttm_tt_create(bo, zero); + if (ret) + goto out_err; + } +@@ -322,14 +280,12 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, + + if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && + !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) +- ret = ttm_bo_move_ttm(bo, ctx->interruptible, +- ctx->no_wait_gpu, mem); ++ ret = ttm_bo_move_ttm(bo, ctx, mem); + else if (bdev->driver->move) + ret = bdev->driver->move(bo, evict, ctx, mem); + else +- ret = ttm_bo_move_memcpy(bo, ctx->interruptible, +- ctx->no_wait_gpu, mem); +- ++ ret = ttm_bo_move_memcpy(bo, ctx, mem); ++ + if (ret) { + if (bdev->driver->move_notify) { + struct ttm_mem_reg tmp_mem = *mem; +@@ -353,13 +309,12 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, + bo->evicted = false; + } + +- if (bo->mem.mm_node) { ++ if (bo->mem.mm_node) + bo->offset = (bo->mem.start << PAGE_SHIFT) + + bdev->man[bo->mem.mem_type].gpu_offset; +- bo->cur_placement = bo->mem.placement; +- } else ++ else + bo->offset = 0; +- ++ + ctx->bytes_moved += bo->num_pages << PAGE_SHIFT; + return 0; + +@@ -389,8 +344,6 @@ static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) + ttm_tt_destroy(bo->ttm); + bo->ttm = NULL; + ttm_bo_mem_put(bo, &bo->mem); +- +- ww_mutex_unlock (&bo->resv->lock); + } + + static int ttm_bo_individualize_resv(struct ttm_buffer_object *bo) +@@ -432,7 +385,7 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo) + static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) + { + struct ttm_bo_device *bdev = bo->bdev; +- struct ttm_bo_global *glob = bo->glob; ++ struct ttm_bo_global *glob = bdev->glob; + int ret; + + ret = ttm_bo_individualize_resv(bo); +@@ -447,7 +400,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) + } + + spin_lock(&glob->lru_lock); +- ret = __ttm_bo_reserve(bo, false, true, NULL); ++ ret = reservation_object_trylock(bo->resv) ? 0 : -EBUSY; + if (!ret) { + if (reservation_object_test_signaled_rcu(&bo->ttm_resv, true)) { + ttm_bo_del_from_lru(bo); +@@ -456,6 +409,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) + reservation_object_unlock(&bo->ttm_resv); + + ttm_bo_cleanup_memtype_use(bo); ++ reservation_object_unlock(bo->resv); + return; + } + +@@ -471,8 +425,8 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) + ttm_bo_add_to_lru(bo); + } + +- __ttm_bo_unreserve(bo); +- } ++ reservation_object_unlock(bo->resv); ++ } + if (bo->resv != &bo->ttm_resv) + reservation_object_unlock(&bo->ttm_resv); + +@@ -497,11 +451,11 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) + * @no_wait_gpu Never wait for gpu. Return -EBUSY instead. + */ + +-static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, +- bool interruptible, +- bool no_wait_gpu) ++static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, ++ bool interruptible, bool no_wait_gpu, ++ bool unlock_resv) + { +- struct ttm_bo_global *glob = bo->glob; ++ struct ttm_bo_global *glob = bo->bdev->glob; + struct reservation_object *resv; + int ret; + +@@ -517,8 +471,10 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, + + if (ret && !no_wait_gpu) { + long lret; +- ww_mutex_unlock(&bo->resv->lock); +- spin_unlock(&glob->lru_lock); ++ ++ if (unlock_resv) ++ reservation_object_unlock(bo->resv); ++ spin_unlock(&glob->lru_lock); + + lret = reservation_object_wait_timeout_rcu(resv, true, + interruptible, +@@ -530,7 +486,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, + return -EBUSY; + + spin_lock(&glob->lru_lock); +- ret = __ttm_bo_reserve(bo, false, true, NULL); ++ if (unlock_resv && !reservation_object_trylock(bo->resv)) { + + /* + * We raced, and lost, someone else holds the reservation now, +@@ -540,14 +496,15 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, + * delayed destruction would succeed, so just return success + * here. + */ +- if (ret) { +- spin_unlock(&glob->lru_lock); ++ spin_unlock(&glob->lru_lock); + return 0; + } ++ ret = 0; + } + + if (ret || unlikely(list_empty(&bo->ddestroy))) { +- __ttm_bo_unreserve(bo); ++ if (unlock_resv) ++ reservation_object_unlock(bo->resv); + spin_unlock(&glob->lru_lock); + return ret; + } +@@ -559,6 +516,9 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, + spin_unlock(&glob->lru_lock); + ttm_bo_cleanup_memtype_use(bo); + ++ if (unlock_resv) ++ reservation_object_unlock(bo->resv); ++ + return 0; + } + +@@ -566,60 +526,44 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, + * Traverse the delayed list, and call ttm_bo_cleanup_refs on all + * encountered buffers. + */ +- +-static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) ++static bool ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) + { + struct ttm_bo_global *glob = bdev->glob; +- struct ttm_buffer_object *entry = NULL; +- int ret = 0; ++ struct list_head removed; ++ bool empty; + +- spin_lock(&glob->lru_lock); +- if (list_empty(&bdev->ddestroy)) +- goto out_unlock; ++ INIT_LIST_HEAD(&removed); + +- entry = list_first_entry(&bdev->ddestroy, +- struct ttm_buffer_object, ddestroy); +- kref_get(&entry->list_kref); +- +- for (;;) { +- struct ttm_buffer_object *nentry = NULL; ++ spin_lock(&glob->lru_lock); ++ while (!list_empty(&bdev->ddestroy)) { ++ struct ttm_buffer_object *bo; + +- if (entry->ddestroy.next != &bdev->ddestroy) { +- nentry = list_first_entry(&entry->ddestroy, +- struct ttm_buffer_object, ddestroy); +- kref_get(&nentry->list_kref); +- } ++ bo = list_first_entry(&bdev->ddestroy, struct ttm_buffer_object, ++ ddestroy); ++ kref_get(&bo->list_kref); ++ list_move_tail(&bo->ddestroy, &removed); + +- ret = __ttm_bo_reserve(entry, false, true, NULL); +- if (remove_all && ret) { ++ if (remove_all || bo->resv != &bo->ttm_resv) { + spin_unlock(&glob->lru_lock); +- ret = __ttm_bo_reserve(entry, false, false, NULL); ++ reservation_object_lock(bo->resv, NULL); ++ + spin_lock(&glob->lru_lock); +- } ++ ttm_bo_cleanup_refs(bo, false, !remove_all, true); + +- if (!ret) +- ret = ttm_bo_cleanup_refs_and_unlock(entry, false, +- !remove_all); +- else ++ } else if (reservation_object_trylock(bo->resv)) { ++ ttm_bo_cleanup_refs(bo, false, !remove_all, true); ++ } else { + spin_unlock(&glob->lru_lock); ++ } + +- kref_put(&entry->list_kref, ttm_bo_release_list); +- entry = nentry; +- +- if (ret || !entry) +- goto out; +- ++ kref_put(&bo->list_kref, ttm_bo_release_list); + spin_lock(&glob->lru_lock); +- if (list_empty(&entry->ddestroy)) +- break; + } +- +-out_unlock: ++ list_splice_tail(&removed, &bdev->ddestroy); ++ empty = list_empty(&bdev->ddestroy); + spin_unlock(&glob->lru_lock); +-out: +- if (entry) +- kref_put(&entry->list_kref, ttm_bo_release_list); +- return ret; ++ ++ return empty; + } + + static void ttm_bo_delayed_workqueue(struct work_struct *work) +@@ -627,10 +571,9 @@ static void ttm_bo_delayed_workqueue(struct work_struct *work) + struct ttm_bo_device *bdev = + container_of(work, struct ttm_bo_device, wq.work); + +- if (ttm_bo_delayed_delete(bdev, false)) { ++ if (!ttm_bo_delayed_delete(bdev, false)) + schedule_delayed_work(&bdev->wq, + ((HZ / 100) < 1) ? 1 : HZ / 100); +- } + } + + static void ttm_bo_release(struct kref *kref) +@@ -679,16 +622,25 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, + struct ttm_placement placement; + int ret = 0; + +- lockdep_assert_held(&bo->resv->lock.base); ++ reservation_object_assert_held(bo->resv); ++ ++ placement.num_placement = 0; ++ placement.num_busy_placement = 0; ++ bdev->driver->evict_flags(bo, &placement); ++ ++ if (!placement.num_placement && !placement.num_busy_placement) { ++ ret = ttm_bo_pipeline_gutting(bo); ++ if (ret) ++ return ret; ++ ++ return ttm_tt_create(bo, false); ++ } + + evict_mem = bo->mem; + evict_mem.mm_node = NULL; + evict_mem.bus.io_reserved_vm = false; + evict_mem.bus.io_reserved_count = 0; + +- placement.num_placement = 0; +- placement.num_busy_placement = 0; +- bdev->driver->evict_flags(bo, &placement); + ret = ttm_bo_mem_space(bo, &placement, &evict_mem, ctx); + if (ret) { + if (ret != -ERESTARTSYS) { +@@ -743,7 +695,8 @@ static bool ttm_bo_evict_swapout_allowable(struct ttm_buffer_object *bo, + *locked = false; + if (bo->resv == ctx->resv) { + reservation_object_assert_held(bo->resv); +- if (ctx->allow_reserved_eviction || !list_empty(&bo->ddestroy)) ++ if (ctx->flags & TTM_OPT_FLAG_ALLOW_RES_EVICT ++ || !list_empty(&bo->ddestroy)) + ret = true; + } else { + *locked = reservation_object_trylock(bo->resv); +@@ -876,7 +829,7 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, + return ret; + if (mem->mm_node) + break; +- ret = ttm_mem_evict_first(bdev, bo->resv, mem_type, place, ctx); ++ ret = ttm_mem_evict_first(bdev, mem_type, place, ctx); + if (unlikely(ret != 0)) + return ret; + } while (1); +@@ -1052,14 +1005,13 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, + EXPORT_SYMBOL(ttm_bo_mem_space); + + static int ttm_bo_move_buffer(struct ttm_buffer_object *bo, +- struct ttm_placement *placement, +- struct ttm_placement *placement, +- struct ttm_operation_ctx *ctx) ++ struct ttm_placement *placement, ++ struct ttm_operation_ctx *ctx) + { + int ret = 0; + struct ttm_mem_reg mem; +- +- lockdep_assert_held(&bo->resv->lock.base); ++ ++ reservation_object_assert_held(bo->resv); + + mem.num_pages = bo->num_pages; + mem.size = mem.num_pages << PAGE_SHIFT; +@@ -1129,7 +1081,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, + int ret; + uint32_t new_flags; + +- lockdep_assert_held(&bo->resv->lock.base); ++ reservation_object_assert_held(bo->resv); + /* + * Check whether we need to move buffer. + */ +@@ -1149,7 +1101,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, + * We might need to add a TTM. + */ + if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { +- ret = ttm_bo_add_ttm(bo, true); ++ ret = ttm_tt_create(bo, true); + if (ret) + return ret; + } +@@ -1194,7 +1146,7 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev, + ttm_mem_global_free(mem_glob, acc_size); + return -EINVAL; + } +- bo->destroy = destroy; ++ bo->destroy = destroy ? destroy : ttm_bo_default_destroy; + + kref_init(&bo->kref); + kref_init(&bo->list_kref); +@@ -1205,7 +1157,6 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev, + INIT_LIST_HEAD(&bo->io_reserve_lru); + mutex_init(&bo->wu_mutex); + bo->bdev = bdev; +- bo->glob = bdev->glob; + bo->type = type; + bo->num_pages = num_pages; + bo->mem.size = num_pages << PAGE_SHIFT; +@@ -1219,14 +1170,15 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev, + bo->mem.placement = (TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED); + bo->acc_size = acc_size; + bo->sg = sg; ++ bo->ssg_can_map = false; + if (resv) { + bo->resv = resv; +- lockdep_assert_held(&bo->resv->lock.base); ++ reservation_object_assert_held(bo->resv); + } else { + bo->resv = &bo->ttm_resv; + } + reservation_object_init(&bo->ttm_resv); +- atomic_inc(&bo->glob->bo_count); ++ atomic_inc(&bo->bdev->glob->bo_count); + drm_vma_node_reset(&bo->vma_node); + bo->priority = 0; + +@@ -1243,15 +1195,12 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev, + * since otherwise lockdep will be angered in radeon. + */ + if (!resv) { +- locked = ww_mutex_trylock(&bo->resv->lock); ++ locked = reservation_object_trylock(bo->resv); + WARN_ON(!locked); + } + +- if (likely(!ret)) { +- struct ttm_operation_ctx ctx = { interruptible, false }; +- +- ret = ttm_bo_validate(bo, placement, &ctx); +- } ++ if (likely(!ret)) ++ ret = ttm_bo_validate(bo, placement, ctx); + + if (unlikely(ret)) { + if (!resv) +@@ -1262,9 +1211,9 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev, + } + + if (resv && !(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { +- spin_lock(&bo->glob->lru_lock); ++ spin_lock(&bdev->glob->lru_lock); + ttm_bo_add_to_lru(bo); +- spin_unlock(&bo->glob->lru_lock); ++ spin_unlock(&bdev->glob->lru_lock); + } + + return ret; +@@ -1357,7 +1306,11 @@ EXPORT_SYMBOL(ttm_bo_create); + static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, + unsigned mem_type) + { +- struct ttm_operation_ctx ctx = { false, false }; ++ struct ttm_operation_ctx ctx = { ++ .interruptible = false, ++ .no_wait_gpu = false, ++ .flags = TTM_OPT_FLAG_FORCE_ALLOC ++ }; + struct ttm_mem_type_manager *man = &bdev->man[mem_type]; + struct ttm_bo_global *glob = bdev->glob; + struct dma_fence *fence; +@@ -1372,8 +1325,7 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { + while (!list_empty(&man->lru[i])) { + spin_unlock(&glob->lru_lock); +- ret = ttm_mem_evict_first(bdev, NULL, mem_type, +- NULL, &ctx); ++ ret = ttm_mem_evict_first(bdev, mem_type, NULL, &ctx); + if (ret) + return ret; + spin_lock(&glob->lru_lock); +@@ -1494,7 +1446,6 @@ static void ttm_bo_global_kobj_release(struct kobject *kobj) + struct ttm_bo_global *glob = + container_of(kobj, struct ttm_bo_global, kobj); + +- ttm_mem_unregister_shrink(glob->mem_glob, &glob->shrink); + __free_page(glob->dummy_read_page); + kfree(glob); + } +@@ -1519,6 +1470,7 @@ int ttm_bo_global_init(struct drm_global_reference *ref) + mutex_init(&glob->device_list_mutex); + spin_lock_init(&glob->lru_lock); + glob->mem_glob = bo_ref->mem_glob; ++ glob->mem_glob->bo_glob = glob; + glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); + + if (unlikely(glob->dummy_read_page == NULL)) { +@@ -1529,14 +1481,6 @@ int ttm_bo_global_init(struct drm_global_reference *ref) + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) + INIT_LIST_HEAD(&glob->swap_lru[i]); + INIT_LIST_HEAD(&glob->device_list); +- +- ttm_mem_init_shrink(&glob->shrink, ttm_bo_swapout); +- ret = ttm_mem_register_shrink(glob->mem_glob, &glob->shrink); +- if (unlikely(ret != 0)) { +- pr_err("Could not register buffer object swapout\n"); +- goto out_no_shrink; +- } +- + atomic_set(&glob->bo_count, 0); + + ret = kobject_init_and_add( +@@ -1544,8 +1488,6 @@ int ttm_bo_global_init(struct drm_global_reference *ref) + if (unlikely(ret != 0)) + kobject_put(&glob->kobj); + return ret; +-out_no_shrink: +- __free_page(glob->dummy_read_page); + out_no_drp: + kfree(glob); + return ret; +@@ -1579,16 +1521,13 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev) + + cancel_delayed_work_sync(&bdev->wq); + +- while (ttm_bo_delayed_delete(bdev, true)) +- ; ++ if (ttm_bo_delayed_delete(bdev, true)) ++ pr_debug("Delayed destroy list was clean\n"); + + spin_lock(&glob->lru_lock); +- if (list_empty(&bdev->ddestroy)) +- TTM_DEBUG("Delayed destroy list was clean\n"); +- + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) + if (list_empty(&bdev->man[0].lru[0])) +- TTM_DEBUG("Swap list %d was clean\n", i); ++ pr_debug("Swap list %d was clean\n", i); + spin_unlock(&glob->lru_lock); + + drm_vma_offset_manager_destroy(&bdev->vma_manager); +@@ -1731,7 +1670,6 @@ EXPORT_SYMBOL(ttm_bo_synccpu_write_release); + * A buffer object shrink method that tries to swap out the first + * buffer object on the bo_global::swap_lru list. + */ +- + int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx) + { + struct ttm_buffer_object *bo; +@@ -1813,7 +1751,7 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx) + * already swapped buffer. + */ + if (locked) +- kcl_reservation_object_unlock(bo->resv); ++ reservation_object_unlock(bo->resv); + kref_put(&bo->list_kref, ttm_bo_release_list); + return ret; + } +@@ -1853,10 +1791,12 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo) + return -ERESTARTSYS; + if (!ww_mutex_is_locked(&bo->resv->lock)) + goto out_unlock; +- ret = __ttm_bo_reserve(bo, true, false, NULL); ++ ret = reservation_object_lock_interruptible(bo->resv, NULL); ++ if (ret == -EINTR) ++ ret = -ERESTARTSYS; + if (unlikely(ret != 0)) + goto out_unlock; +- __ttm_bo_unreserve(bo); ++ reservation_object_unlock(bo->resv); + + out_unlock: + mutex_unlock(&bo->wu_mutex); +diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c +index 4ef7a59..de9161f 100644 +--- a/drivers/gpu/drm/ttm/ttm_bo_util.c ++++ b/drivers/gpu/drm/ttm/ttm_bo_util.c +@@ -45,7 +45,7 @@ void ttm_bo_free_old_node(struct ttm_buffer_object *bo) + } + + int ttm_bo_move_ttm(struct ttm_buffer_object *bo, +- bool interruptible, bool no_wait_gpu, ++ struct ttm_operation_ctx *ctx, + struct ttm_mem_reg *new_mem) + { + struct ttm_tt *ttm = bo->ttm; +@@ -53,7 +53,7 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo, + int ret; + + if (old_mem->mem_type != TTM_PL_SYSTEM) { +- ret = ttm_bo_wait(bo, interruptible, no_wait_gpu); ++ ret = ttm_bo_wait(bo, ctx->interruptible, ctx->no_wait_gpu); + + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) +@@ -329,7 +329,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst, + } + + int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, +- bool interruptible, bool no_wait_gpu, ++ struct ttm_operation_ctx *ctx, + struct ttm_mem_reg *new_mem) + { + struct ttm_bo_device *bdev = bo->bdev; +@@ -345,7 +345,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, + unsigned long add = 0; + int dir; + +- ret = ttm_bo_wait(bo, interruptible, no_wait_gpu); ++ ret = ttm_bo_wait(bo, ctx->interruptible, ctx->no_wait_gpu); + if (ret) + return ret; + +@@ -375,8 +375,8 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, + /* + * TTM might be null for moves within the same region. + */ +- if (ttm && ttm->state == tt_unpopulated) { +- ret = ttm->bdev->driver->ttm_tt_populate(ttm, ctx); ++ if (ttm) { ++ ret = ttm_tt_populate(ttm, ctx); + if (ret) + goto out1; + } +@@ -402,8 +402,9 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, + PAGE_KERNEL); + ret = ttm_copy_io_ttm_page(ttm, old_iomap, page, + prot); +- } else ++ } else { + ret = ttm_copy_io_page(new_iomap, old_iomap, page); ++ } + if (ret) + goto out1; + } +@@ -469,7 +470,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, + * TODO: Explicit member copy would probably be better here. + */ + +- atomic_inc(&bo->glob->bo_count); ++ atomic_inc(&bo->bdev->glob->bo_count); + INIT_LIST_HEAD(&fbo->ddestroy); + INIT_LIST_HEAD(&fbo->lru); + INIT_LIST_HEAD(&fbo->swap); +@@ -485,7 +486,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, + fbo->acc_size = 0; + fbo->resv = &fbo->ttm_resv; + reservation_object_init(fbo->resv); +- ret = ww_mutex_trylock(&fbo->resv->lock); ++ ret = reservation_object_trylock(fbo->resv); + WARN_ON(!ret); + + *new_obj = fbo; +@@ -556,11 +557,9 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, + + BUG_ON(!ttm); + +- if (ttm->state == tt_unpopulated) { +- ret = ttm->bdev->driver->ttm_tt_populate(ttm, &ctx); +- if (ret) +- return ret; +- } ++ ret = ttm_tt_populate(ttm, &ctx); ++ if (ret) ++ return ret; + + if (num_pages == 1 && (mem->placement & TTM_PL_FLAG_CACHED)) { + /* +@@ -593,7 +592,6 @@ int ttm_bo_kmap(struct ttm_buffer_object *bo, + unsigned long offset, size; + int ret; + +- BUG_ON(!list_empty(&bo->swap)); + map->virtual = NULL; + map->bo = bo; + if (num_pages > bo->num_pages) +@@ -803,3 +801,27 @@ int ttm_bo_pipeline_move(struct ttm_buffer_object *bo, + return 0; + } + EXPORT_SYMBOL(ttm_bo_pipeline_move); ++ ++int ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo) ++{ ++ struct ttm_buffer_object *ghost; ++ int ret; ++ ++ ret = ttm_buffer_object_transfer(bo, &ghost); ++ if (ret) ++ return ret; ++ ++ ret = reservation_object_copy_fences(ghost->resv, bo->resv); ++ /* Last resort, wait for the BO to be idle when we are OOM */ ++ if (ret) ++ ttm_bo_wait(bo, false, false); ++ ++ memset(&bo->mem, 0, sizeof(bo->mem)); ++ bo->mem.mem_type = TTM_PL_SYSTEM; ++ bo->ttm = NULL; ++ ++ ttm_bo_unreserve(ghost); ++ ttm_bo_unref(&ghost); ++ ++ return 0; ++} +diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c +index 77e6577..a18131e 100644 +--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c ++++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c +@@ -92,6 +92,17 @@ static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, + return ret; + } + ++static unsigned long ttm_bo_io_mem_pfn(struct ttm_buffer_object *bo, ++ unsigned long page_offset) ++{ ++ struct ttm_bo_device *bdev = bo->bdev; ++ ++ if (bdev->driver->io_mem_pfn) ++ return bdev->driver->io_mem_pfn(bo, page_offset); ++ ++ return ((bo->mem.bus.base + bo->mem.bus.offset) >> PAGE_SHIFT) ++ + page_offset; ++} + static int ttm_bo_vm_fault(struct vm_fault *vmf) + { + struct vm_area_struct *vma = vmf->vma; +@@ -125,7 +136,7 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) { + if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { + ttm_bo_reference(bo); +- up_read(&vmf->vma->vm_mm->mmap_sem); ++ up_read(&vma->vm_mm->mmap_sem); + (void) ttm_bo_wait_unreserved(bo); + ttm_bo_unref(&bo); + } +@@ -146,7 +157,7 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + * (if at all) by redirecting mmap to the exporter. + */ + if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) { +- retval = VM_FAULT_SIGBUS; ++ ret = VM_FAULT_SIGBUS; + goto out_unlock; + } + +@@ -157,10 +168,10 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + break; + case -EBUSY: + case -ERESTARTSYS: +- retval = VM_FAULT_NOPAGE; ++ ret = VM_FAULT_NOPAGE; + goto out_unlock; + default: +- retval = VM_FAULT_SIGBUS; ++ ret = VM_FAULT_SIGBUS; + goto out_unlock; + } + } +@@ -171,12 +182,10 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + */ + ret = ttm_bo_vm_fault_idle(bo, vmf); + if (unlikely(ret != 0)) { +- retval = ret; +- +- if (retval == VM_FAULT_RETRY && ++ if (ret == VM_FAULT_RETRY && + !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { + /* The BO has already been unreserved. */ +- return retval; ++ return ret; + } + + goto out_unlock; +@@ -184,12 +193,12 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + + ret = ttm_mem_io_lock(man, true); + if (unlikely(ret != 0)) { +- retval = VM_FAULT_NOPAGE; ++ ret = VM_FAULT_NOPAGE; + goto out_unlock; + } + ret = ttm_mem_io_reserve_vm(bo); + if (unlikely(ret != 0)) { +- retval = VM_FAULT_SIGBUS; ++ ret = VM_FAULT_SIGBUS; + goto out_io_unlock; + } + +@@ -199,7 +208,7 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + drm_vma_node_start(&bo->vma_node); + + if (unlikely(page_offset >= bo->num_pages)) { +- retval = VM_FAULT_SIGBUS; ++ ret = VM_FAULT_SIGBUS; + goto out_io_unlock; + } + +@@ -217,7 +226,9 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + } else { + struct ttm_operation_ctx ctx = { + .interruptible = false, +- .no_wait_gpu = false ++ .no_wait_gpu = false, ++ .flags = TTM_OPT_FLAG_FORCE_ALLOC ++ + }; + + ttm = bo->ttm; +@@ -225,8 +236,8 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + cvma.vm_page_prot); + + /* Allocate all page at once, most common usage */ +- if (ttm->bdev->driver->ttm_tt_populate(ttm, &ctx)) { +- retval = VM_FAULT_OOM; ++ if (ttm_tt_populate(ttm, &ctx)) { ++ ret = VM_FAULT_OOM; + goto out_io_unlock; + } + } +@@ -239,16 +250,15 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + if (bo->mem.bus.is_iomem) { + /* Iomem should not be marked encrypted */ + cvma.vm_page_prot = pgprot_decrypted(cvma.vm_page_prot); +- pfn = bdev->driver->io_mem_pfn(bo, page_offset); ++ pfn = ttm_bo_io_mem_pfn(bo, page_offset); + } else { + page = ttm->pages[page_offset]; + if (unlikely(!page && i == 0)) { +- retval = VM_FAULT_OOM; ++ ret = VM_FAULT_OOM; + goto out_io_unlock; + } else if (unlikely(!page)) { + break; + } +- page->mapping = vma->vm_file->f_mapping; + page->index = drm_vma_node_start(&bo->vma_node) + + page_offset; + pfn = page_to_pfn(page); +@@ -256,7 +266,7 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + + if (vma->vm_flags & VM_MIXEDMAP) + ret = vm_insert_mixed(&cvma, address, +- __pfn_to_pfn_t(pfn, PFN_DEV)); ++ __pfn_to_pfn_t(pfn, PFN_DEV | (bo->ssg_can_map ? PFN_MAP : 0))); + else + ret = vm_insert_pfn(&cvma, address, pfn); + +@@ -268,7 +278,7 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + if (unlikely((ret == -EBUSY) || (ret != 0 && i > 0))) + break; + else if (unlikely(ret != 0)) { +- retval = ++ ret = + (ret == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS; + goto out_io_unlock; + } +@@ -277,11 +287,12 @@ static int ttm_bo_vm_fault(struct vm_fault *vmf) + if (unlikely(++page_offset >= page_last)) + break; + } ++ ret = VM_FAULT_NOPAGE; + out_io_unlock: + ttm_mem_io_unlock(man); + out_unlock: + ttm_bo_unreserve(bo); +- return retval; ++ return ret; + } + + static void ttm_bo_vm_open(struct vm_area_struct *vma) +@@ -410,14 +421,6 @@ static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev, + return bo; + } + +-unsigned long ttm_bo_default_io_mem_pfn(struct ttm_buffer_object *bo, +- unsigned long page_offset) +-{ +- return ((bo->mem.bus.base + bo->mem.bus.offset) >> PAGE_SHIFT) +- + page_offset; +-} +-EXPORT_SYMBOL(ttm_bo_default_io_mem_pfn); +- + int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, + struct ttm_bo_device *bdev) + { +@@ -455,7 +458,7 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, + * VM_MIXEDMAP on all mappings. See freedesktop.org bug #75719 + */ + vma->vm_flags |= VM_MIXEDMAP; +- vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; ++ vma->vm_flags |= (bo->ssg_can_map ? 0 : VM_IO) | VM_DONTEXPAND | VM_DONTDUMP; + return 0; + out_unref: + ttm_bo_unref(&bo); +diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c +index 5e1bcab..3dca206e 100644 +--- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c ++++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c +@@ -38,7 +38,7 @@ static void ttm_eu_backoff_reservation_reverse(struct list_head *list, + list_for_each_entry_continue_reverse(entry, list, head) { + struct ttm_buffer_object *bo = entry->bo; + +- __ttm_bo_unreserve(bo); ++ reservation_object_unlock(bo->resv); + } + } + +@@ -62,14 +62,14 @@ void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket, + return; + + entry = list_first_entry(list, struct ttm_validate_buffer, head); +- glob = entry->bo->glob; ++ glob = entry->bo->bdev->glob; + + spin_lock(&glob->lru_lock); + list_for_each_entry(entry, list, head) { + struct ttm_buffer_object *bo = entry->bo; + + ttm_bo_add_to_lru(bo); +- __ttm_bo_unreserve(bo); ++ reservation_object_unlock(bo->resv); + } + spin_unlock(&glob->lru_lock); + +@@ -102,7 +102,7 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, + return 0; + + entry = list_first_entry(list, struct ttm_validate_buffer, head); +- glob = entry->bo->glob; ++ glob = entry->bo->bdev->glob; + + if (ticket) + ww_acquire_init(ticket, &reservation_ww_class); +@@ -112,7 +112,7 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, + + ret = __ttm_bo_reserve(bo, intr, (ticket == NULL), ticket); + if (!ret && unlikely(atomic_read(&bo->cpu_writers) > 0)) { +- __ttm_bo_unreserve(bo); ++ reservation_object_unlock(bo->resv); + + ret = -EBUSY; + +@@ -139,12 +139,14 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, + */ + ttm_eu_backoff_reservation_reverse(list, entry); + +- if (ret == -EDEADLK && intr) { +- ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock, +- ticket); +- } else if (ret == -EDEADLK) { +- ww_mutex_lock_slow(&bo->resv->lock, ticket); +- ret = 0; ++ if (ret == -EDEADLK) { ++ if (intr) { ++ ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock, ++ ticket); ++ } else { ++ ww_mutex_lock_slow(&bo->resv->lock, ticket); ++ ret = 0; ++ } + } + + if (!ret && entry->shared) +@@ -192,7 +194,7 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket, + bo = list_first_entry(list, struct ttm_validate_buffer, head)->bo; + bdev = bo->bdev; + driver = bdev->driver; +- glob = bo->glob; ++ glob = bo->bdev->glob; + + spin_lock(&glob->lru_lock); + +@@ -203,7 +205,7 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket, + else + reservation_object_add_excl_fence(bo->resv, fence); + ttm_bo_add_to_lru(bo); +- __ttm_bo_unreserve(bo); ++ reservation_object_unlock(bo->resv); + } + spin_unlock(&glob->lru_lock); + if (ticket) +diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c +index dc25904..5bd82a9 100644 +--- a/drivers/gpu/drm/ttm/ttm_memory.c ++++ b/drivers/gpu/drm/ttm/ttm_memory.c +@@ -36,6 +36,7 @@ + #include <linux/mm.h> + #include <linux/module.h> + #include <linux/slab.h> ++#include <linux/swap.h> + + #define TTM_MEMORY_ALLOC_RETRIES 4 + +@@ -166,6 +167,54 @@ static struct kobj_type ttm_mem_zone_kobj_type = { + .default_attrs = ttm_mem_zone_attrs, + }; + ++static struct attribute ttm_mem_global_lower_mem_limit = { ++ .name = "lower_mem_limit", ++ .mode = S_IRUGO | S_IWUSR ++}; ++ ++static ssize_t ttm_mem_global_show(struct kobject *kobj, ++ struct attribute *attr, ++ char *buffer) ++{ ++ struct ttm_mem_global *glob = ++ container_of(kobj, struct ttm_mem_global, kobj); ++ uint64_t val = 0; ++ ++ spin_lock(&glob->lock); ++ val = glob->lower_mem_limit; ++ spin_unlock(&glob->lock); ++ /* convert from number of pages to KB */ ++ val <<= (PAGE_SHIFT - 10); ++ return snprintf(buffer, PAGE_SIZE, "%llu\n", ++ (unsigned long long) val); ++} ++ ++static ssize_t ttm_mem_global_store(struct kobject *kobj, ++ struct attribute *attr, ++ const char *buffer, ++ size_t size) ++{ ++ int chars; ++ uint64_t val64; ++ unsigned long val; ++ struct ttm_mem_global *glob = ++ container_of(kobj, struct ttm_mem_global, kobj); ++ ++ chars = sscanf(buffer, "%lu", &val); ++ if (chars == 0) ++ return size; ++ ++ val64 = val; ++ /* convert from KB to number of pages */ ++ val64 >>= (PAGE_SHIFT - 10); ++ ++ spin_lock(&glob->lock); ++ glob->lower_mem_limit = val64; ++ spin_unlock(&glob->lock); ++ ++ return size; ++} ++ + static void ttm_mem_global_kobj_release(struct kobject *kobj) + { + struct ttm_mem_global *glob = +@@ -174,8 +223,20 @@ static void ttm_mem_global_kobj_release(struct kobject *kobj) + kfree(glob); + } + ++static struct attribute *ttm_mem_global_attrs[] = { ++ &ttm_mem_global_lower_mem_limit, ++ NULL ++}; ++ ++static const struct sysfs_ops ttm_mem_global_ops = { ++ .show = &ttm_mem_global_show, ++ .store = &ttm_mem_global_store, ++}; ++ + static struct kobj_type ttm_mem_glob_kobj_type = { + .release = &ttm_mem_global_kobj_release, ++ .sysfs_ops = &ttm_mem_global_ops, ++ .default_attrs = ttm_mem_global_attrs, + }; + + static bool ttm_zones_above_swap_target(struct ttm_mem_global *glob, +@@ -209,34 +270,35 @@ static bool ttm_zones_above_swap_target(struct ttm_mem_global *glob, + * Note that this function is reentrant: + * many threads may try to swap out at any given time. + */ ++ + static void ttm_shrink(struct ttm_mem_global *glob, bool from_wq, +- uint64_t extra, struct ttm_operation_ctx *ctx) ++ uint64_t extra, struct ttm_operation_ctx *ctx) + { +- int ret; ++ int ret; + +- spin_lock(&glob->lock); ++ spin_lock(&glob->lock); + +- while (ttm_zones_above_swap_target(glob, from_wq, extra)) { +- spin_unlock(&glob->lock); +- ret = ttm_bo_swapout(glob->bo_glob, ctx); +- spin_lock(&glob->lock); +- if (unlikely(ret != 0)) +- break; +- } ++ while (ttm_zones_above_swap_target(glob, from_wq, extra)) { ++ spin_unlock(&glob->lock); ++ ret = ttm_bo_swapout(glob->bo_glob, ctx); ++ spin_lock(&glob->lock); ++ if (unlikely(ret != 0)) ++ break; ++ } + +- spin_unlock(&glob->lock); ++ spin_unlock(&glob->lock); + } + + static void ttm_shrink_work(struct work_struct *work) + { +- struct ttm_operation_ctx ctx = { +- .interruptible = false, +- .no_wait_gpu = false +- }; +- struct ttm_mem_global *glob = +- container_of(work, struct ttm_mem_global, work); +- +- ttm_shrink(glob, true, 0ULL, &ctx); ++ struct ttm_operation_ctx ctx = { ++ .interruptible = false, ++ .no_wait_gpu = false ++ }; ++ struct ttm_mem_global *glob = ++ container_of(work, struct ttm_mem_global, work); ++ ++ ttm_shrink(glob, true, 0ULL, &ctx); + } + + static int ttm_mem_init_kernel_zone(struct ttm_mem_global *glob, +@@ -374,6 +436,9 @@ int ttm_mem_global_init(struct ttm_mem_global *glob) + + si_meminfo(&si); + ++ /* set it as 0 by default to keep original behavior of OOM */ ++ glob->lower_mem_limit = 0; ++ + ret = ttm_mem_init_kernel_zone(glob, &si); + if (unlikely(ret != 0)) + goto out_no_zone; +@@ -468,6 +533,36 @@ void ttm_mem_global_free(struct ttm_mem_global *glob, + } + EXPORT_SYMBOL(ttm_mem_global_free); + ++/* ++ * check if the available mem is under lower memory limit ++ * ++ * a. if no swap disk at all or free swap space is under swap_mem_limit ++ * but available system mem is bigger than sys_mem_limit, allow TTM ++ * allocation; ++ * ++ * b. if the available system mem is less than sys_mem_limit but free ++ * swap disk is bigger than swap_mem_limit, allow TTM allocation. ++ */ ++bool ++ttm_check_under_lowerlimit(struct ttm_mem_global *glob, ++ uint64_t num_pages, ++ struct ttm_operation_ctx *ctx) ++{ ++ int64_t available; ++ ++ if (ctx->flags & TTM_OPT_FLAG_FORCE_ALLOC) ++ return false; ++ ++ available = get_nr_swap_pages() + si_mem_available(); ++ ++ available -= num_pages; ++ if (available < glob->lower_mem_limit) ++ return true; ++ ++ return false; ++} ++EXPORT_SYMBOL(ttm_check_under_lowerlimit); ++ + static int ttm_mem_global_reserve(struct ttm_mem_global *glob, + struct ttm_mem_zone *single_zone, + uint64_t amount, bool reserve) +@@ -542,10 +637,9 @@ int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, + EXPORT_SYMBOL(ttm_mem_global_alloc); + + int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, +- struct page *page, uint64_t size, +- struct ttm_operation_ctx *ctx) ++ struct page *page, uint64_t size, ++ struct ttm_operation_ctx *ctx) + { +- + struct ttm_mem_zone *zone = NULL; + + /** +@@ -563,7 +657,8 @@ int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, + return ttm_mem_global_alloc_zone(glob, zone, size, ctx); + } + +-void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page) ++void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page, ++ uint64_t size) + { + struct ttm_mem_zone *zone = NULL; + +@@ -574,7 +669,7 @@ void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page) + if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) + zone = glob->zone_kernel; + #endif +- ttm_mem_global_free_zone(glob, zone, PAGE_SIZE); ++ ttm_mem_global_free_zone(glob, zone, size); + } + + +diff --git a/drivers/gpu/drm/ttm/ttm_module.c b/drivers/gpu/drm/ttm/ttm_module.c +index 66fc639..7370e34 100644 +--- a/drivers/gpu/drm/ttm/ttm_module.c ++++ b/drivers/gpu/drm/ttm/ttm_module.c +@@ -38,7 +38,7 @@ static DECLARE_WAIT_QUEUE_HEAD(exit_q); + static atomic_t device_released; + + static struct device_type ttm_drm_class_type = { +- .name = "ttm", ++ .name = "amdttm", + /** + * Add pm ops here. + */ +@@ -66,7 +66,7 @@ static int __init ttm_init(void) + { + int ret; + +- ret = dev_set_name(&ttm_drm_class_device, "ttm"); ++ ret = dev_set_name(&ttm_drm_class_device, "amdttm"); + if (unlikely(ret != 0)) + return ret; + +diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c +index 55c9a90..9f9f181 100644 +--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c ++++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c +@@ -81,6 +81,7 @@ struct ttm_page_pool { + char *name; + unsigned long nfrees; + unsigned long nrefills; ++ unsigned int order; + }; + + /** +@@ -95,7 +96,7 @@ struct ttm_pool_opts { + unsigned small; + }; + +-#define NUM_POOLS 4 ++#define NUM_POOLS 6 + + /** + * struct ttm_pool_manager - Holds memory pools for fst allocation +@@ -122,6 +123,8 @@ struct ttm_pool_manager { + struct ttm_page_pool uc_pool; + struct ttm_page_pool wc_pool_dma32; + struct ttm_page_pool uc_pool_dma32; ++ struct ttm_page_pool wc_pool_huge; ++ struct ttm_page_pool uc_pool_huge; + } ; + }; + }; +@@ -220,6 +223,17 @@ static struct kobj_type ttm_pool_kobj_type = { + static struct ttm_pool_manager *_manager; + + #ifndef CONFIG_X86 ++static int set_pages_wb(struct page *page, int numpages) ++{ ++#if IS_ENABLED(CONFIG_AGP) ++ int i; ++ ++ for (i = 0; i < numpages; i++) ++ unmap_page_from_agp(page++); ++#endif ++ return 0; ++} ++ + static int set_pages_array_wb(struct page **pages, int addrinarray) + { + #if IS_ENABLED(CONFIG_AGP) +@@ -256,8 +270,8 @@ static int set_pages_array_uc(struct page **pages, int addrinarray) + + /** + * Select the right pool or requested caching state and ttm flags. */ +-static struct ttm_page_pool *ttm_get_pool(int flags, +- enum ttm_caching_state cstate) ++static struct ttm_page_pool *ttm_get_pool(int flags, bool huge, ++ enum ttm_caching_state cstate) + { + int pool_index; + +@@ -269,20 +283,36 @@ static struct ttm_page_pool *ttm_get_pool(int flags, + else + pool_index = 0x1; + +- if (flags & TTM_PAGE_FLAG_DMA32) ++ if (flags & TTM_PAGE_FLAG_DMA32) { ++ if (huge) ++ return NULL; + pool_index |= 0x2; + ++ } else if (huge) { ++ pool_index |= 0x4; ++ } ++ + return &_manager->pools[pool_index]; + } + + /* set memory back to wb and free the pages. */ +-static void ttm_pages_put(struct page *pages[], unsigned npages) ++static void ttm_pages_put(struct page *pages[], unsigned npages, ++ unsigned int order) + { +- unsigned i; +- if (set_pages_array_wb(pages, npages)) +- pr_err("Failed to set %d pages to wb!\n", npages); +- for (i = 0; i < npages; ++i) +- __free_page(pages[i]); ++ unsigned int i, pages_nr = (1 << order); ++ ++ if (order == 0) { ++ if (set_pages_array_wb(pages, npages)) ++ pr_err("Failed to set %d pages to wb!\n", npages); ++ } ++ ++ for (i = 0; i < npages; ++i) { ++ if (order > 0) { ++ if (set_pages_wb(pages[i], pages_nr)) ++ pr_err("Failed to set %d pages to wb!\n", pages_nr); ++ } ++ __free_pages(pages[i], order); ++ } + } + + static void ttm_pool_update_free_locked(struct ttm_page_pool *pool, +@@ -321,7 +351,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free, + pages_to_free = kmalloc(npages_to_free * sizeof(struct page *), + GFP_KERNEL); + if (!pages_to_free) { +- pr_err("Failed to allocate memory for pool free operation\n"); ++ pr_debug("Failed to allocate memory for pool free operation\n"); + return 0; + } + +@@ -345,7 +375,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free, + */ + spin_unlock_irqrestore(&pool->lock, irq_flags); + +- ttm_pages_put(pages_to_free, freed_pages); ++ ttm_pages_put(pages_to_free, freed_pages, pool->order); + if (likely(nr_free != FREE_ALL_PAGES)) + nr_free -= freed_pages; + +@@ -380,7 +410,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free, + spin_unlock_irqrestore(&pool->lock, irq_flags); + + if (freed_pages) +- ttm_pages_put(pages_to_free, freed_pages); ++ ttm_pages_put(pages_to_free, freed_pages, pool->order); + out: + if (pages_to_free != static_buf) + kfree(pages_to_free); +@@ -404,6 +434,7 @@ ttm_pool_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) + struct ttm_page_pool *pool; + int shrink_pages = sc->nr_to_scan; + unsigned long freed = 0; ++ unsigned int nr_free_pool; + + if (!mutex_trylock(&lock)) + return SHRINK_STOP; +@@ -411,12 +442,20 @@ ttm_pool_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) + /* select start pool in round robin fashion */ + for (i = 0; i < NUM_POOLS; ++i) { + unsigned nr_free = shrink_pages; ++ unsigned page_nr; ++ + if (shrink_pages == 0) + break; ++ + pool = &_manager->pools[(i + pool_offset)%NUM_POOLS]; ++ page_nr = (1 << pool->order); + /* OK to use static buffer since global mutex is held. */ +- shrink_pages = ttm_page_pool_free(pool, nr_free, true); +- freed += nr_free - shrink_pages; ++ nr_free_pool = roundup(nr_free, page_nr) >> pool->order; ++ shrink_pages = ttm_page_pool_free(pool, nr_free_pool, true); ++ freed += (nr_free_pool - shrink_pages) << pool->order; ++ if (freed >= sc->nr_to_scan) ++ break; ++ shrink_pages <<= pool->order; + } + mutex_unlock(&lock); + return freed; +@@ -428,19 +467,22 @@ ttm_pool_shrink_count(struct shrinker *shrink, struct shrink_control *sc) + { + unsigned i; + unsigned long count = 0; ++ struct ttm_page_pool *pool; + +- for (i = 0; i < NUM_POOLS; ++i) +- count += _manager->pools[i].npages; ++ for (i = 0; i < NUM_POOLS; ++i) { ++ pool = &_manager->pools[i]; ++ count += (pool->npages << pool->order); ++ } + + return count; + } + +-static void ttm_pool_mm_shrink_init(struct ttm_pool_manager *manager) ++static int ttm_pool_mm_shrink_init(struct ttm_pool_manager *manager) + { + manager->mm_shrink.count_objects = ttm_pool_shrink_count; + manager->mm_shrink.scan_objects = ttm_pool_shrink_scan; + manager->mm_shrink.seeks = 1; +- register_shrinker(&manager->mm_shrink); ++ return register_shrinker(&manager->mm_shrink); + } + + static void ttm_pool_mm_shrink_fini(struct ttm_pool_manager *manager) +@@ -494,28 +536,29 @@ static void ttm_handle_caching_state_failure(struct list_head *pages, + * pages returned in pages array. + */ + static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags, +- int ttm_flags, enum ttm_caching_state cstate, unsigned count) ++ int ttm_flags, enum ttm_caching_state cstate, ++ unsigned count, unsigned order) + { + struct page **caching_array; + struct page *p; + int r = 0; +- unsigned i, cpages; +- unsigned max_cpages = min(count, +- (unsigned)(PAGE_SIZE/sizeof(struct page *))); ++ unsigned i, j, cpages; ++ unsigned npages = 1 << order; ++ unsigned max_cpages = min(count << order, (unsigned)NUM_PAGES_TO_ALLOC); + + /* allocate array for page caching change */ + caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL); + + if (!caching_array) { +- pr_err("Unable to allocate table for new pages\n"); ++ pr_debug("Unable to allocate table for new pages\n"); + return -ENOMEM; + } + + for (i = 0, cpages = 0; i < count; ++i) { +- p = alloc_page(gfp_flags); ++ p = alloc_pages(gfp_flags, order); + + if (!p) { +- pr_err("Unable to get page %u\n", i); ++ pr_debug("Unable to get page %u\n", i); + + /* store already allocated pages in the pool after + * setting the caching state */ +@@ -531,14 +574,18 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags, + goto out; + } + ++ list_add(&p->lru, pages); ++ + #ifdef CONFIG_HIGHMEM + /* gfp flags of highmem page should never be dma32 so we + * we should be fine in such case + */ +- if (!PageHighMem(p)) ++ if (PageHighMem(p)) ++ continue; ++ + #endif +- { +- caching_array[cpages++] = p; ++ for (j = 0; j < npages; ++j) { ++ caching_array[cpages++] = p++; + if (cpages == max_cpages) { + + r = ttm_set_pages_caching(caching_array, +@@ -552,8 +599,6 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags, + cpages = 0; + } + } +- +- list_add(&p->lru, pages); + } + + if (cpages) { +@@ -573,9 +618,9 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags, + * Fill the given pool if there aren't enough pages and the requested number of + * pages is small. + */ +-static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, +- int ttm_flags, enum ttm_caching_state cstate, unsigned count, +- unsigned long *irq_flags) ++static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, int ttm_flags, ++ enum ttm_caching_state cstate, ++ unsigned count, unsigned long *irq_flags) + { + struct page *p; + int r; +@@ -605,7 +650,7 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, + + INIT_LIST_HEAD(&new_pages); + r = ttm_alloc_new_pages(&new_pages, pool->gfp_flags, ttm_flags, +- cstate, alloc_size); ++ cstate, alloc_size, 0); + spin_lock_irqsave(&pool->lock, *irq_flags); + + if (!r) { +@@ -613,7 +658,7 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, + ++pool->nrefills; + pool->npages += alloc_size; + } else { +- pr_err("Failed to fill pool (%p)\n", pool); ++ pr_debug("Failed to fill pool (%p)\n", pool); + /* If we have any pages left put them to the pool. */ + list_for_each_entry(p, &new_pages, lru) { + ++cpages; +@@ -627,22 +672,25 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, + } + + /** +- * Cut 'count' number of pages from the pool and put them on the return list. ++ * Allocate pages from the pool and put them on the return list. + * +- * @return count of pages still required to fulfill the request. ++ * @return zero for success or negative error code. + */ +-static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool, +- struct list_head *pages, +- int ttm_flags, +- enum ttm_caching_state cstate, +- unsigned count) ++static int ttm_page_pool_get_pages(struct ttm_page_pool *pool, ++ struct list_head *pages, ++ int ttm_flags, ++ enum ttm_caching_state cstate, ++ unsigned count, unsigned order) + { + unsigned long irq_flags; + struct list_head *p; + unsigned i; ++ int r = 0; + + spin_lock_irqsave(&pool->lock, irq_flags); +- ttm_page_pool_fill_locked(pool, ttm_flags, cstate, count, &irq_flags); ++ if (!order) ++ ttm_page_pool_fill_locked(pool, ttm_flags, cstate, count, ++ &irq_flags); + + if (count >= pool->npages) { + /* take all pages from the pool */ +@@ -672,32 +720,131 @@ static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool, + count = 0; + out: + spin_unlock_irqrestore(&pool->lock, irq_flags); +- return count; ++ ++ /* clear the pages coming from the pool if requested */ ++ if (ttm_flags & TTM_PAGE_FLAG_ZERO_ALLOC) { ++ struct page *page; ++ ++ list_for_each_entry(page, pages, lru) { ++ if (PageHighMem(page)) ++ clear_highpage(page); ++ else ++ clear_page(page_address(page)); ++ } ++ } ++ ++ /* If pool didn't have enough pages allocate new one. */ ++ if (count) { ++ gfp_t gfp_flags = pool->gfp_flags; ++ ++ /* set zero flag for page allocation if required */ ++ if (ttm_flags & TTM_PAGE_FLAG_ZERO_ALLOC) ++ gfp_flags |= __GFP_ZERO; ++ ++ if (ttm_flags & TTM_PAGE_FLAG_NO_RETRY) ++ gfp_flags |= __GFP_RETRY_MAYFAIL; ++ ++ /* ttm_alloc_new_pages doesn't reference pool so we can run ++ * multiple requests in parallel. ++ **/ ++ r = ttm_alloc_new_pages(pages, gfp_flags, ttm_flags, cstate, ++ count, order); ++ } ++ ++ return r; + } + + /* Put all pages in pages list to correct pool to wait for reuse */ + static void ttm_put_pages(struct page **pages, unsigned npages, int flags, + enum ttm_caching_state cstate) + { ++ struct ttm_page_pool *pool = ttm_get_pool(flags, false, cstate); ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ struct ttm_page_pool *huge = ttm_get_pool(flags, true, cstate); ++#endif + unsigned long irq_flags; +- struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); + unsigned i; + + if (pool == NULL) { + /* No pool for this memory type so free the pages */ +- for (i = 0; i < npages; i++) { +- if (pages[i]) { +- if (page_count(pages[i]) != 1) +- pr_err("Erroneous page count. Leaking pages.\n"); +- __free_page(pages[i]); +- pages[i] = NULL; ++ i = 0; ++ while (i < npages) { ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ struct page *p = pages[i]; ++#endif ++ unsigned order = 0, j; ++ ++ if (!pages[i]) { ++ ++i; ++ continue; ++ } ++ ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ if (!(flags & TTM_PAGE_FLAG_DMA32)) { ++ for (j = 0; j < HPAGE_PMD_NR; ++j) ++ if (p++ != pages[i + j]) ++ break; ++ ++ if (j == HPAGE_PMD_NR) ++ order = HPAGE_PMD_ORDER; ++ } ++#endif ++ ++ if (page_count(pages[i]) != 1) ++ pr_err("Erroneous page count. Leaking pages.\n"); ++ __free_pages(pages[i], order); ++ ++ j = 1 << order; ++ while (j) { ++ pages[i++] = NULL; ++ --j; + } + } + return; + } + ++ i = 0; ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ if (huge) { ++ unsigned max_size, n2free; ++ ++ spin_lock_irqsave(&huge->lock, irq_flags); ++ while (i < npages) { ++ struct page *p = pages[i]; ++ unsigned j; ++ ++ if (!p) ++ break; ++ ++ for (j = 0; j < HPAGE_PMD_NR; ++j) ++ if (p++ != pages[i + j]) ++ break; ++ ++ if (j != HPAGE_PMD_NR) ++ break; ++ ++ list_add_tail(&pages[i]->lru, &huge->list); ++ ++ for (j = 0; j < HPAGE_PMD_NR; ++j) ++ pages[i++] = NULL; ++ huge->npages++; ++ } ++ ++ /* Check that we don't go over the pool limit */ ++ max_size = _manager->options.max_size; ++ max_size /= HPAGE_PMD_NR; ++ if (huge->npages > max_size) ++ n2free = huge->npages - max_size; ++ else ++ n2free = 0; ++ spin_unlock_irqrestore(&huge->lock, irq_flags); ++ if (n2free) ++ ttm_page_pool_free(huge, n2free, false); ++ } ++#endif ++ + spin_lock_irqsave(&pool->lock, irq_flags); +- for (i = 0; i < npages; i++) { ++ while (i < npages) { + if (pages[i]) { + if (page_count(pages[i]) != 1) + pr_err("Erroneous page count. Leaking pages.\n"); +@@ -705,6 +852,7 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags, + pages[i] = NULL; + pool->npages++; + } ++ ++i; + } + /* Check that we don't go over the pool limit */ + npages = 0; +@@ -727,82 +875,120 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags, + static int ttm_get_pages(struct page **pages, unsigned npages, int flags, + enum ttm_caching_state cstate) + { +- struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); ++ struct ttm_page_pool *pool = ttm_get_pool(flags, false, cstate); ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ struct ttm_page_pool *huge = ttm_get_pool(flags, true, cstate); ++#endif + struct list_head plist; + struct page *p = NULL; +- gfp_t gfp_flags = GFP_USER; +- unsigned count; ++ unsigned count, first; + int r; + +- /* set zero flag for page allocation if required */ +- if (flags & TTM_PAGE_FLAG_ZERO_ALLOC) +- gfp_flags |= __GFP_ZERO; +- + /* No pool for cached pages */ + if (pool == NULL) { ++ gfp_t gfp_flags = GFP_USER; ++ unsigned i; ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ unsigned j; ++#endif ++ ++ /* set zero flag for page allocation if required */ ++ if (flags & TTM_PAGE_FLAG_ZERO_ALLOC) ++ gfp_flags |= __GFP_ZERO; ++ ++ if (flags & TTM_PAGE_FLAG_NO_RETRY) ++ gfp_flags |= __GFP_RETRY_MAYFAIL; ++ + if (flags & TTM_PAGE_FLAG_DMA32) + gfp_flags |= GFP_DMA32; + else + gfp_flags |= GFP_HIGHUSER; + +- for (r = 0; r < npages; ++r) { ++ i = 0; ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ if (!(gfp_flags & GFP_DMA32)) { ++ while (npages >= HPAGE_PMD_NR) { ++ gfp_t huge_flags = gfp_flags; ++ ++ huge_flags |= GFP_TRANSHUGE; ++ huge_flags &= ~__GFP_MOVABLE; ++ huge_flags &= ~__GFP_COMP; ++ p = alloc_pages(huge_flags, HPAGE_PMD_ORDER); ++ if (!p) ++ break; ++ ++ for (j = 0; j < HPAGE_PMD_NR; ++j) ++ pages[i++] = p++; ++ ++ npages -= HPAGE_PMD_NR; ++ } ++ } ++#endif ++ ++ first = i; ++ while (npages) { + p = alloc_page(gfp_flags); + if (!p) { +- +- pr_err("Unable to allocate page\n"); ++ pr_debug("Unable to allocate page\n"); + return -ENOMEM; + } + +- pages[r] = p; ++ /* Swap the pages if we detect consecutive order */ ++ if (i > first && pages[i - 1] == p - 1) ++ swap(p, pages[i - 1]); ++ ++ pages[i++] = p; ++ --npages; + } + return 0; + } + +- /* combine zero flag to pool flags */ +- gfp_flags |= pool->gfp_flags; +- +- /* First we take pages from the pool */ +- INIT_LIST_HEAD(&plist); +- npages = ttm_page_pool_get_pages(pool, &plist, flags, cstate, npages); + count = 0; +- list_for_each_entry(p, &plist, lru) { +- pages[count++] = p; +- } + +- /* clear the pages coming from the pool if requested */ +- if (flags & TTM_PAGE_FLAG_ZERO_ALLOC) { ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ if (huge && npages >= HPAGE_PMD_NR) { ++ INIT_LIST_HEAD(&plist); ++ ttm_page_pool_get_pages(huge, &plist, flags, cstate, ++ npages / HPAGE_PMD_NR, ++ HPAGE_PMD_ORDER); ++ + list_for_each_entry(p, &plist, lru) { +- if (PageHighMem(p)) +- clear_highpage(p); +- else +- clear_page(page_address(p)); ++ unsigned j; ++ ++ for (j = 0; j < HPAGE_PMD_NR; ++j) ++ pages[count++] = &p[j]; + } + } ++#endif + +- /* If pool didn't have enough pages allocate new one. */ +- if (npages > 0) { +- /* ttm_alloc_new_pages doesn't reference pool so we can run +- * multiple requests in parallel. +- **/ +- INIT_LIST_HEAD(&plist); +- r = ttm_alloc_new_pages(&plist, gfp_flags, flags, cstate, npages); +- list_for_each_entry(p, &plist, lru) { +- pages[count++] = p; +- } +- if (r) { +- /* If there is any pages in the list put them back to +- * the pool. */ +- pr_err("Failed to allocate extra pages for large request\n"); +- ttm_put_pages(pages, count, flags, cstate); +- return r; +- } ++ INIT_LIST_HEAD(&plist); ++ r = ttm_page_pool_get_pages(pool, &plist, flags, cstate, ++ npages - count, 0); ++ ++ first = count; ++ list_for_each_entry(p, &plist, lru) { ++ struct page *tmp = p; ++ ++ /* Swap the pages if we detect consecutive order */ ++ if (count > first && pages[count - 1] == tmp - 1) ++ swap(tmp, pages[count - 1]); ++ pages[count++] = tmp; ++ } ++ ++ if (r) { ++ /* If there is any pages in the list put them back to ++ * the pool. ++ */ ++ pr_debug("Failed to allocate extra pages for large request\n"); ++ ttm_put_pages(pages, count, flags, cstate); ++ return r; + } + + return 0; + } + + static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, gfp_t flags, +- char *name) ++ char *name, unsigned int order) + { + spin_lock_init(&pool->lock); + pool->fill_lock = false; +@@ -810,11 +996,17 @@ static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, gfp_t flags, + pool->npages = pool->nfrees = 0; + pool->gfp_flags = flags; + pool->name = name; ++ pool->order = order; + } + + int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) + { + int ret; ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ unsigned order = HPAGE_PMD_ORDER; ++#else ++ unsigned order = 0; ++#endif + + WARN_ON(_manager); + +@@ -824,31 +1016,41 @@ int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) + if (!_manager) + return -ENOMEM; + +- ttm_page_pool_init_locked(&_manager->wc_pool, GFP_HIGHUSER, "wc"); ++ ttm_page_pool_init_locked(&_manager->wc_pool, GFP_HIGHUSER, "wc", 0); + +- ttm_page_pool_init_locked(&_manager->uc_pool, GFP_HIGHUSER, "uc"); ++ ttm_page_pool_init_locked(&_manager->uc_pool, GFP_HIGHUSER, "uc", 0); + + ttm_page_pool_init_locked(&_manager->wc_pool_dma32, +- GFP_USER | GFP_DMA32, "wc dma"); +- ++ GFP_USER | GFP_DMA32, "wc dma", 0); + ttm_page_pool_init_locked(&_manager->uc_pool_dma32, +- GFP_USER | GFP_DMA32, "uc dma"); ++ GFP_USER | GFP_DMA32, "uc dma", 0); + ++ ttm_page_pool_init_locked(&_manager->wc_pool_huge, ++ GFP_TRANSHUGE & ~(__GFP_MOVABLE | __GFP_COMP), ++ "wc huge", order); ++ ++ ttm_page_pool_init_locked(&_manager->uc_pool_huge, ++ GFP_TRANSHUGE & ~(__GFP_MOVABLE | __GFP_COMP) ++ , "uc huge", order); ++ + _manager->options.max_size = max_pages; + _manager->options.small = SMALL_ALLOCATION; + _manager->options.alloc_size = NUM_PAGES_TO_ALLOC; + + ret = kobject_init_and_add(&_manager->kobj, &ttm_pool_kobj_type, + &glob->kobj, "pool"); +- if (unlikely(ret != 0)) { +- kobject_put(&_manager->kobj); +- _manager = NULL; +- return ret; +- } +- +- ttm_pool_mm_shrink_init(_manager); ++ if (unlikely(ret != 0)) ++ goto error; + ++ ret = ttm_pool_mm_shrink_init(_manager); ++ if (unlikely(ret != 0)) ++ goto error; + return 0; ++ ++error: ++ kobject_put(&_manager->kobj); ++ _manager = NULL; ++ return ret; + } + + void ttm_page_alloc_fini(void) +@@ -866,28 +1068,52 @@ void ttm_page_alloc_fini(void) + _manager = NULL; + } + ++static void ++ttm_pool_unpopulate_helper(struct ttm_tt *ttm, unsigned mem_count_update) ++{ ++ struct ttm_mem_global *mem_glob = ttm->bdev->glob->mem_glob; ++ unsigned i; ++ ++ if (mem_count_update == 0) ++ goto put_pages; ++ ++ for (i = 0; i < mem_count_update; ++i) { ++ if (!ttm->pages[i]) ++ continue; ++ ++ ttm_mem_global_free_page(mem_glob, ttm->pages[i], PAGE_SIZE); ++ } ++ ++put_pages: ++ ttm_put_pages(ttm->pages, ttm->num_pages, ttm->page_flags, ++ ttm->caching_state); ++ ttm->state = tt_unpopulated; ++} ++ + int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx) + { +- struct ttm_mem_global *mem_glob = ttm->glob->mem_glob; ++ struct ttm_mem_global *mem_glob = ttm->bdev->glob->mem_glob; + unsigned i; + int ret; + + if (ttm->state != tt_unpopulated) + return 0; + +- for (i = 0; i < ttm->num_pages; ++i) { +- ret = ttm_get_pages(&ttm->pages[i], 1, +- ttm->page_flags, +- ttm->caching_state); +- if (ret != 0) { +- ttm_pool_unpopulate(ttm); +- return -ENOMEM; +- } ++ if (ttm_check_under_lowerlimit(mem_glob, ttm->num_pages, ctx)) ++ return -ENOMEM; ++ ++ ret = ttm_get_pages(ttm->pages, ttm->num_pages, ttm->page_flags, ++ ttm->caching_state); ++ if (unlikely(ret != 0)) { ++ ttm_pool_unpopulate_helper(ttm, 0); ++ return ret; ++ } + ++ for (i = 0; i < ttm->num_pages; ++i) { + ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], +- PAGE_SIZE, ctx); ++ PAGE_SIZE, ctx); + if (unlikely(ret != 0)) { +- ttm_pool_unpopulate(ttm); ++ ttm_pool_unpopulate_helper(ttm, i); + return -ENOMEM; + } + } +@@ -907,34 +1133,33 @@ EXPORT_SYMBOL(ttm_pool_populate); + + void ttm_pool_unpopulate(struct ttm_tt *ttm) + { +- unsigned i; +- +- for (i = 0; i < ttm->num_pages; ++i) { +- if (ttm->pages[i]) { +- ttm_mem_global_free_page(ttm->glob->mem_glob, +- ttm->pages[i]); +- ttm_put_pages(&ttm->pages[i], 1, +- ttm->page_flags, +- ttm->caching_state); +- } +- } +- ttm->state = tt_unpopulated; ++ ttm_pool_unpopulate_helper(ttm, ttm->num_pages); + } + EXPORT_SYMBOL(ttm_pool_unpopulate); + + int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt, + struct ttm_operation_ctx *ctx) + { +- unsigned i; ++ unsigned i, j; + int r; + + r = ttm_pool_populate(&tt->ttm, ctx); + if (r) + return r; + +- for (i = 0; i < tt->ttm.num_pages; i++) { ++ for (i = 0; i < tt->ttm.num_pages; ++i) { ++ struct page *p = tt->ttm.pages[i]; ++ size_t num_pages = 1; ++ ++ for (j = i + 1; j < tt->ttm.num_pages; ++j) { ++ if (++p != tt->ttm.pages[j]) ++ break; ++ ++ ++num_pages; ++ } ++ + tt->dma_address[i] = dma_map_page(dev, tt->ttm.pages[i], +- 0, PAGE_SIZE, ++ 0, num_pages * PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, tt->dma_address[i])) { + while (i--) { +@@ -945,6 +1170,11 @@ int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt, + ttm_pool_unpopulate(&tt->ttm); + return -EFAULT; + } ++ ++ for (j = 1; j < num_pages; ++j) { ++ tt->dma_address[i + 1] = tt->dma_address[i] + PAGE_SIZE; ++ ++i; ++ } + } + return 0; + } +@@ -952,13 +1182,28 @@ EXPORT_SYMBOL(ttm_populate_and_map_pages); + + void ttm_unmap_and_unpopulate_pages(struct device *dev, struct ttm_dma_tt *tt) + { +- unsigned i; +- +- for (i = 0; i < tt->ttm.num_pages; i++) { +- if (tt->dma_address[i]) { +- dma_unmap_page(dev, tt->dma_address[i], +- PAGE_SIZE, DMA_BIDIRECTIONAL); ++ unsigned i, j; ++ ++ for (i = 0; i < tt->ttm.num_pages;) { ++ struct page *p = tt->ttm.pages[i]; ++ size_t num_pages = 1; ++ ++ if (!tt->dma_address[i] || !tt->ttm.pages[i]) { ++ ++i; ++ continue; ++ } ++ ++ for (j = i + 1; j < tt->ttm.num_pages; ++j) { ++ if (++p != tt->ttm.pages[j]) ++ break; ++ ++ ++num_pages; + } ++ ++ dma_unmap_page(dev, tt->dma_address[i], num_pages * PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ ++ i += num_pages; + } + ttm_pool_unpopulate(&tt->ttm); + } +@@ -973,12 +1218,12 @@ int ttm_page_alloc_debugfs(struct seq_file *m, void *data) + seq_printf(m, "No pool allocator running.\n"); + return 0; + } +- seq_printf(m, "%6s %12s %13s %8s\n", ++ seq_printf(m, "%7s %12s %13s %8s\n", + h[0], h[1], h[2], h[3]); + for (i = 0; i < NUM_POOLS; ++i) { + p = &_manager->pools[i]; + +- seq_printf(m, "%6s %12ld %13ld %8d\n", ++ seq_printf(m, "%7s %12ld %13ld %8d\n", + p->name, p->nrefills, + p->nfrees, p->npages); + } +diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +index 47ba7be..52fccc5 100644 +--- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c ++++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +@@ -60,37 +60,32 @@ + #define NUM_PAGES_TO_ALLOC (PAGE_SIZE/sizeof(struct page *)) + #define SMALL_ALLOCATION 4 + #define FREE_ALL_PAGES (~0U) +-/* times are in msecs */ +-#define IS_UNDEFINED (0) +-#define IS_WC (1<<1) +-#define IS_UC (1<<2) +-#define IS_CACHED (1<<3) +-#define IS_DMA32 (1<<4) ++#define VADDR_FLAG_HUGE_POOL 1UL ++#define VADDR_FLAG_UPDATED_COUNT 2UL + + enum pool_type { +- POOL_IS_UNDEFINED, +- POOL_IS_WC = IS_WC, +- POOL_IS_UC = IS_UC, +- POOL_IS_CACHED = IS_CACHED, +- POOL_IS_WC_DMA32 = IS_WC | IS_DMA32, +- POOL_IS_UC_DMA32 = IS_UC | IS_DMA32, +- POOL_IS_CACHED_DMA32 = IS_CACHED | IS_DMA32, ++ IS_UNDEFINED = 0, ++ IS_WC = 1 << 1, ++ IS_UC = 1 << 2, ++ IS_CACHED = 1 << 3, ++ IS_DMA32 = 1 << 4, ++ IS_HUGE = 1 << 5 + }; + /* +- * The pool structure. There are usually six pools: ++ * The pool structure. There are up to nine pools: + * - generic (not restricted to DMA32): + * - write combined, uncached, cached. + * - dma32 (up to 2^32 - so up 4GB): + * - write combined, uncached, cached. ++ * - huge (not restricted to DMA32): ++ * - write combined, uncached, cached. + * for each 'struct device'. The 'cached' is for pages that are actively used. + * The other ones can be shrunk by the shrinker API if neccessary. + * @pools: The 'struct device->dma_pools' link. + * @type: Type of the pool +- * @lock: Protects the inuse_list and free_list from concurrnet access. Must be ++ * @lock: Protects the free_list from concurrnet access. Must be + * used with irqsave/irqrestore variants because pool allocator maybe called + * from delayed work. +- * @inuse_list: Pool of pages that are in use. The order is very important and +- * it is in the order that the TTM pages that are put back are in. + * @free_list: Pool of pages that are free to be used. No order requirements. + * @dev: The device that is associated with these pools. + * @size: Size used during DMA allocation. +@@ -107,7 +102,6 @@ struct dma_pool { + struct list_head pools; /* The 'struct device->dma_pools link */ + enum pool_type type; + spinlock_t lock; +- struct list_head inuse_list; + struct list_head free_list; + struct device *dev; + unsigned size; +@@ -124,13 +118,14 @@ struct dma_pool { + * The accounting page keeping track of the allocated page along with + * the DMA address. + * @page_list: The link to the 'page_list' in 'struct dma_pool'. +- * @vaddr: The virtual address of the page ++ * @vaddr: The virtual address of the page and a flag if the page belongs to a ++ * huge pool + * @dma: The bus address of the page. If the page is not allocated + * via the DMA API, it will be -1. + */ + struct dma_page { + struct list_head page_list; +- void *vaddr; ++ unsigned long vaddr; + struct page *p; + dma_addr_t dma; + }; +@@ -221,11 +216,11 @@ static ssize_t ttm_pool_store(struct kobject *kobj, struct attribute *attr, + /* Convert kb to number of pages */ + val = val / (PAGE_SIZE >> 10); + +- if (attr == &ttm_page_pool_max) ++ if (attr == &ttm_page_pool_max) { + m->options.max_size = val; +- else if (attr == &ttm_page_pool_small) ++ } else if (attr == &ttm_page_pool_small) { + m->options.small = val; +- else if (attr == &ttm_page_pool_alloc_size) { ++ } else if (attr == &ttm_page_pool_alloc_size) { + if (val > NUM_PAGES_TO_ALLOC*8) { + pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n", + NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7), +@@ -329,7 +324,8 @@ static int ttm_set_pages_caching(struct dma_pool *pool, + static void __ttm_dma_free_page(struct dma_pool *pool, struct dma_page *d_page) + { + dma_addr_t dma = d_page->dma; +- dma_free_coherent(pool->dev, pool->size, d_page->vaddr, dma); ++ d_page->vaddr &= ~VADDR_FLAG_HUGE_POOL; ++ dma_free_coherent(pool->dev, pool->size, (void *)d_page->vaddr, dma); + + kfree(d_page); + d_page = NULL; +@@ -337,19 +333,26 @@ static void __ttm_dma_free_page(struct dma_pool *pool, struct dma_page *d_page) + static struct dma_page *__ttm_dma_alloc_page(struct dma_pool *pool) + { + struct dma_page *d_page; ++ unsigned long attrs = 0; ++ void *vaddr; + + d_page = kmalloc(sizeof(struct dma_page), GFP_KERNEL); + if (!d_page) + return NULL; + +- d_page->vaddr = dma_alloc_coherent(pool->dev, pool->size, +- &d_page->dma, +- pool->gfp_flags); +- if (d_page->vaddr) { +- if (is_vmalloc_addr(d_page->vaddr)) +- d_page->p = vmalloc_to_page(d_page->vaddr); ++ if (pool->type & IS_HUGE) ++ attrs = DMA_ATTR_NO_WARN; ++ ++ vaddr = dma_alloc_attrs(pool->dev, pool->size, &d_page->dma, ++ pool->gfp_flags, attrs); ++ if (vaddr) { ++ if (is_vmalloc_addr(vaddr)) ++ d_page->p = vmalloc_to_page(vaddr); + else +- d_page->p = virt_to_page(d_page->vaddr); ++ d_page->p = virt_to_page(vaddr); ++ d_page->vaddr = (unsigned long)vaddr; ++ if (pool->type & IS_HUGE) ++ d_page->vaddr |= VADDR_FLAG_HUGE_POOL; + } else { + kfree(d_page); + d_page = NULL; +@@ -381,11 +384,38 @@ static void ttm_pool_update_free_locked(struct dma_pool *pool, + } + + /* set memory back to wb and free the pages. */ ++static void ttm_dma_page_put(struct dma_pool *pool, struct dma_page *d_page) ++{ ++ struct page *page = d_page->p; ++ unsigned i, num_pages; ++ ++ /* Don't set WB on WB page pool. */ ++ if (!(pool->type & IS_CACHED)) { ++ num_pages = pool->size / PAGE_SIZE; ++ for (i = 0; i < num_pages; ++i, ++page) { ++ if (set_pages_array_wb(&page, 1)) { ++ pr_err("%s: Failed to set %d pages to wb!\n", ++ pool->dev_name, 1); ++ } ++ } ++ } ++ ++ list_del(&d_page->page_list); ++ __ttm_dma_free_page(pool, d_page); ++} ++ + static void ttm_dma_pages_put(struct dma_pool *pool, struct list_head *d_pages, + struct page *pages[], unsigned npages) + { + struct dma_page *d_page, *tmp; + ++ if (pool->type & IS_HUGE) { ++ list_for_each_entry_safe(d_page, tmp, d_pages, page_list) ++ ttm_dma_page_put(pool, d_page); ++ ++ return; ++ } ++ + /* Don't set WB on WB page pool. */ + if (npages && !(pool->type & IS_CACHED) && + set_pages_array_wb(pages, npages)) +@@ -398,17 +428,6 @@ static void ttm_dma_pages_put(struct dma_pool *pool, struct list_head *d_pages, + } + } + +-static void ttm_dma_page_put(struct dma_pool *pool, struct dma_page *d_page) +-{ +- /* Don't set WB on WB page pool. */ +- if (!(pool->type & IS_CACHED) && set_pages_array_wb(&d_page->p, 1)) +- pr_err("%s: Failed to set %d pages to wb!\n", +- pool->dev_name, 1); +- +- list_del(&d_page->page_list); +- __ttm_dma_free_page(pool, d_page); +-} +- + /* + * Free pages from pool. + * +@@ -446,7 +465,7 @@ static unsigned ttm_dma_page_pool_free(struct dma_pool *pool, unsigned nr_free, + GFP_KERNEL); + + if (!pages_to_free) { +- pr_err("%s: Failed to allocate memory for pool free operation\n", ++ pr_debug("%s: Failed to allocate memory for pool free operation\n", + pool->dev_name); + return 0; + } +@@ -577,8 +596,8 @@ static int ttm_dma_pool_match(struct device *dev, void *res, void *match_data) + static struct dma_pool *ttm_dma_pool_init(struct device *dev, gfp_t flags, + enum pool_type type) + { +- char *n[] = {"wc", "uc", "cached", " dma32", "unknown",}; +- enum pool_type t[] = {IS_WC, IS_UC, IS_CACHED, IS_DMA32, IS_UNDEFINED}; ++ const char *n[] = {"wc", "uc", "cached", " dma32", "huge"}; ++ enum pool_type t[] = {IS_WC, IS_UC, IS_CACHED, IS_DMA32, IS_HUGE}; + struct device_pools *sec_pool = NULL; + struct dma_pool *pool = NULL, **ptr; + unsigned i; +@@ -609,18 +628,24 @@ static struct dma_pool *ttm_dma_pool_init(struct device *dev, gfp_t flags, + sec_pool->pool = pool; + + INIT_LIST_HEAD(&pool->free_list); +- INIT_LIST_HEAD(&pool->inuse_list); + INIT_LIST_HEAD(&pool->pools); + spin_lock_init(&pool->lock); + pool->dev = dev; + pool->npages_free = pool->npages_in_use = 0; + pool->nfrees = 0; + pool->gfp_flags = flags; +- pool->size = PAGE_SIZE; ++ if (type & IS_HUGE) ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ pool->size = HPAGE_PMD_SIZE; ++#else ++ BUG(); ++#endif ++ else ++ pool->size = PAGE_SIZE; + pool->type = type; + pool->nrefills = 0; + p = pool->name; +- for (i = 0; i < 5; i++) { ++ for (i = 0; i < ARRAY_SIZE(t); i++) { + if (type & t[i]) { + p += snprintf(p, sizeof(pool->name) - (p - pool->name), + "%s", n[i]); +@@ -653,10 +678,10 @@ static struct dma_pool *ttm_dma_pool_init(struct device *dev, gfp_t flags, + static struct dma_pool *ttm_dma_find_pool(struct device *dev, + enum pool_type type) + { +- struct dma_pool *pool, *tmp, *found = NULL; ++ struct dma_pool *pool, *tmp; + + if (type == IS_UNDEFINED) +- return found; ++ return NULL; + + /* NB: We iterate on the 'struct dev' which has no spinlock, but + * it does have a kref which we have taken. The kref is taken during +@@ -669,13 +694,10 @@ static struct dma_pool *ttm_dma_find_pool(struct device *dev, + * thing is at that point of time there are no pages associated with the + * driver so this function will not be called. + */ +- list_for_each_entry_safe(pool, tmp, &dev->dma_pools, pools) { +- if (pool->type != type) +- continue; +- found = pool; +- break; +- } +- return found; ++ list_for_each_entry_safe(pool, tmp, &dev->dma_pools, pools) ++ if (pool->type == type) ++ return pool; ++ return NULL; + } + + /* +@@ -724,7 +746,7 @@ static int ttm_dma_pool_alloc_new_pages(struct dma_pool *pool, + struct dma_page *dma_p; + struct page *p; + int r = 0; +- unsigned i, cpages; ++ unsigned i, j, npages, cpages; + unsigned max_cpages = min(count, + (unsigned)(PAGE_SIZE/sizeof(struct page *))); + +@@ -732,21 +754,20 @@ static int ttm_dma_pool_alloc_new_pages(struct dma_pool *pool, + caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL); + + if (!caching_array) { +- pr_err("%s: Unable to allocate table for new pages\n", ++ pr_debug("%s: Unable to allocate table for new pages\n", + pool->dev_name); + return -ENOMEM; + } + +- if (count > 1) { ++ if (count > 1) + pr_debug("%s: (%s:%d) Getting %d pages\n", + pool->dev_name, pool->name, current->pid, count); +- } + + for (i = 0, cpages = 0; i < count; ++i) { + dma_p = __ttm_dma_alloc_page(pool); + if (!dma_p) { +- pr_err("%s: Unable to get page %u\n", +- pool->dev_name, i); ++ pr_debug("%s: Unable to get page %u\n", ++ pool->dev_name, i); + + /* store already allocated pages in the pool after + * setting the caching state */ +@@ -762,28 +783,32 @@ static int ttm_dma_pool_alloc_new_pages(struct dma_pool *pool, + goto out; + } + p = dma_p->p; ++ list_add(&dma_p->page_list, d_pages); ++ + #ifdef CONFIG_HIGHMEM + /* gfp flags of highmem page should never be dma32 so we + * we should be fine in such case + */ +- if (!PageHighMem(p)) ++ if (PageHighMem(p)) ++ continue; + #endif +- { +- caching_array[cpages++] = p; ++ ++ npages = pool->size / PAGE_SIZE; ++ for (j = 0; j < npages; ++j) { ++ caching_array[cpages++] = p + j; + if (cpages == max_cpages) { + /* Note: Cannot hold the spinlock */ + r = ttm_set_pages_caching(pool, caching_array, +- cpages); ++ cpages); + if (r) { + ttm_dma_handle_caching_state_failure( +- pool, d_pages, caching_array, +- cpages); ++ pool, d_pages, caching_array, ++ cpages); + goto out; + } + cpages = 0; + } + } +- list_add(&dma_p->page_list, d_pages); + } + + if (cpages) { +@@ -828,8 +853,8 @@ static int ttm_dma_page_pool_fill_locked(struct dma_pool *pool, + struct dma_page *d_page; + unsigned cpages = 0; + +- pr_err("%s: Failed to fill %s pool (r:%d)!\n", +- pool->dev_name, pool->name, r); ++ pr_debug("%s: Failed to fill %s pool (r:%d)!\n", ++ pool->dev_name, pool->name, r); + + list_for_each_entry(d_page, &d_pages, page_list) { + cpages++; +@@ -843,18 +868,18 @@ static int ttm_dma_page_pool_fill_locked(struct dma_pool *pool, + } + + /* +- * @return count of pages still required to fulfill the request. + * The populate list is actually a stack (not that is matters as TTM + * allocates one page at a time. ++ * return dma_page pointer if success, otherwise NULL. + */ +-static int ttm_dma_pool_get_pages(struct dma_pool *pool, ++static struct dma_page *ttm_dma_pool_get_pages(struct dma_pool *pool, + struct ttm_dma_tt *ttm_dma, + unsigned index) + { +- struct dma_page *d_page; ++ struct dma_page *d_page = NULL; + struct ttm_tt *ttm = &ttm_dma->ttm; + unsigned long irq_flags; +- int count, r = -ENOMEM; ++ int count; + + spin_lock_irqsave(&pool->lock, irq_flags); + count = ttm_dma_page_pool_fill_locked(pool, &irq_flags); +@@ -863,12 +888,35 @@ static int ttm_dma_pool_get_pages(struct dma_pool *pool, + ttm->pages[index] = d_page->p; + ttm_dma->dma_address[index] = d_page->dma; + list_move_tail(&d_page->page_list, &ttm_dma->pages_list); +- r = 0; + pool->npages_in_use += 1; + pool->npages_free -= 1; + } + spin_unlock_irqrestore(&pool->lock, irq_flags); +- return r; ++ return d_page; ++} ++ ++static gfp_t ttm_dma_pool_gfp_flags(struct ttm_dma_tt *ttm_dma, bool huge) ++{ ++ struct ttm_tt *ttm = &ttm_dma->ttm; ++ gfp_t gfp_flags; ++ ++ if (ttm->page_flags & TTM_PAGE_FLAG_DMA32) ++ gfp_flags = GFP_USER | GFP_DMA32; ++ else ++ gfp_flags = GFP_HIGHUSER; ++ if (ttm->page_flags & TTM_PAGE_FLAG_ZERO_ALLOC) ++ gfp_flags |= __GFP_ZERO; ++ ++ if (huge) { ++ gfp_flags |= GFP_TRANSHUGE; ++ gfp_flags &= ~__GFP_MOVABLE; ++ gfp_flags &= ~__GFP_COMP; ++ } ++ ++ if (ttm->page_flags & TTM_PAGE_FLAG_NO_RETRY) ++ gfp_flags |= __GFP_RETRY_MAYFAIL; ++ ++ return gfp_flags; + } + + /* +@@ -879,37 +927,78 @@ int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev, + struct ttm_operation_ctx *ctx) + { + struct ttm_tt *ttm = &ttm_dma->ttm; +- struct ttm_mem_global *mem_glob = ttm->glob->mem_glob; ++ struct ttm_mem_global *mem_glob = ttm->bdev->glob->mem_glob; + unsigned long num_pages = ttm->num_pages; + struct dma_pool *pool; ++ struct dma_page *d_page; + enum pool_type type; + unsigned i; +- gfp_t gfp_flags; + int ret; + + if (ttm->state != tt_unpopulated) + return 0; + ++ if (ttm_check_under_lowerlimit(mem_glob, num_pages, ctx)) ++ return -ENOMEM; ++ ++ INIT_LIST_HEAD(&ttm_dma->pages_list); ++ i = 0; ++ + type = ttm_to_type(ttm->page_flags, ttm->caching_state); ++ ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE + if (ttm->page_flags & TTM_PAGE_FLAG_DMA32) +- gfp_flags = GFP_USER | GFP_DMA32; +- else +- gfp_flags = GFP_HIGHUSER; +- if (ttm->page_flags & TTM_PAGE_FLAG_ZERO_ALLOC) +- gfp_flags |= __GFP_ZERO; ++ goto skip_huge; ++ ++ pool = ttm_dma_find_pool(dev, type | IS_HUGE); ++ if (!pool) { ++ gfp_t gfp_flags = ttm_dma_pool_gfp_flags(ttm_dma, true); ++ ++ pool = ttm_dma_pool_init(dev, gfp_flags, type | IS_HUGE); ++ if (IS_ERR_OR_NULL(pool)) ++ goto skip_huge; ++ } ++ ++ while (num_pages >= HPAGE_PMD_NR) { ++ unsigned j; ++ ++ d_page = ttm_dma_pool_get_pages(pool, ttm_dma, i); ++ if (!d_page) ++ break; ++ ++ ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], ++ pool->size, ctx); ++ if (unlikely(ret != 0)) { ++ ttm_dma_unpopulate(ttm_dma, dev); ++ return -ENOMEM; ++ } ++ ++ d_page->vaddr |= VADDR_FLAG_UPDATED_COUNT; ++ for (j = i + 1; j < (i + HPAGE_PMD_NR); ++j) { ++ ttm->pages[j] = ttm->pages[j - 1] + 1; ++ ttm_dma->dma_address[j] = ttm_dma->dma_address[j - 1] + ++ PAGE_SIZE; ++ } ++ ++ i += HPAGE_PMD_NR; ++ num_pages -= HPAGE_PMD_NR; ++ } ++ ++skip_huge: ++#endif + + pool = ttm_dma_find_pool(dev, type); + if (!pool) { ++ gfp_t gfp_flags = ttm_dma_pool_gfp_flags(ttm_dma, false); ++ + pool = ttm_dma_pool_init(dev, gfp_flags, type); +- if (IS_ERR_OR_NULL(pool)) { ++ if (IS_ERR_OR_NULL(pool)) + return -ENOMEM; +- } + } + +- INIT_LIST_HEAD(&ttm_dma->pages_list); +- for (i = 0; i < ttm->num_pages; ++i) { +- ret = ttm_dma_pool_get_pages(pool, ttm_dma, i); +- if (ret != 0) { ++ while (num_pages) { ++ d_page = ttm_dma_pool_get_pages(pool, ttm_dma, i); ++ if (!d_page) { + ttm_dma_unpopulate(ttm_dma, dev); + return -ENOMEM; + } +@@ -920,6 +1009,10 @@ int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev, + ttm_dma_unpopulate(ttm_dma, dev); + return -ENOMEM; + } ++ ++ d_page->vaddr |= VADDR_FLAG_UPDATED_COUNT; ++ ++i; ++ --num_pages; + } + + if (unlikely(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)) { +@@ -939,14 +1032,41 @@ EXPORT_SYMBOL_GPL(ttm_dma_populate); + void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev) + { + struct ttm_tt *ttm = &ttm_dma->ttm; ++ struct ttm_mem_global *mem_glob = ttm->bdev->glob->mem_glob; + struct dma_pool *pool; + struct dma_page *d_page, *next; + enum pool_type type; + bool is_cached = false; +- unsigned count = 0, i, npages = 0; ++ unsigned count, i, npages = 0; + unsigned long irq_flags; + + type = ttm_to_type(ttm->page_flags, ttm->caching_state); ++ ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ pool = ttm_dma_find_pool(dev, type | IS_HUGE); ++ if (pool) { ++ count = 0; ++ list_for_each_entry_safe(d_page, next, &ttm_dma->pages_list, ++ page_list) { ++ if (!(d_page->vaddr & VADDR_FLAG_HUGE_POOL)) ++ continue; ++ ++ count++; ++ if (d_page->vaddr & VADDR_FLAG_UPDATED_COUNT) { ++ ttm_mem_global_free_page(mem_glob, d_page->p, ++ pool->size); ++ d_page->vaddr &= ~VADDR_FLAG_UPDATED_COUNT; ++ } ++ ttm_dma_page_put(pool, d_page); ++ } ++ ++ spin_lock_irqsave(&pool->lock, irq_flags); ++ pool->npages_in_use -= count; ++ pool->nfrees += count; ++ spin_unlock_irqrestore(&pool->lock, irq_flags); ++ } ++#endif ++ + pool = ttm_dma_find_pool(dev, type); + if (!pool) + return; +@@ -955,9 +1075,20 @@ void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev) + ttm_to_type(ttm->page_flags, tt_cached)) == pool); + + /* make sure pages array match list and count number of pages */ +- list_for_each_entry(d_page, &ttm_dma->pages_list, page_list) { ++ count = 0; ++ list_for_each_entry_safe(d_page, next, &ttm_dma->pages_list, ++ page_list) { + ttm->pages[count] = d_page->p; + count++; ++ ++ if (d_page->vaddr & VADDR_FLAG_UPDATED_COUNT) { ++ ttm_mem_global_free_page(mem_glob, d_page->p, ++ pool->size); ++ d_page->vaddr &= ~VADDR_FLAG_UPDATED_COUNT; ++ } ++ ++ if (is_cached) ++ ttm_dma_page_put(pool, d_page); + } + + spin_lock_irqsave(&pool->lock, irq_flags); +@@ -977,19 +1108,6 @@ void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev) + } + spin_unlock_irqrestore(&pool->lock, irq_flags); + +- if (is_cached) { +- list_for_each_entry_safe(d_page, next, &ttm_dma->pages_list, page_list) { +- ttm_mem_global_free_page(ttm->glob->mem_glob, +- d_page->p); +- ttm_dma_page_put(pool, d_page); +- } +- } else { +- for (i = 0; i < count; i++) { +- ttm_mem_global_free_page(ttm->glob->mem_glob, +- ttm->pages[i]); +- } +- } +- + INIT_LIST_HEAD(&ttm_dma->pages_list); + for (i = 0; i < ttm->num_pages; i++) { + ttm->pages[i] = NULL; +@@ -1067,12 +1185,12 @@ ttm_dma_pool_shrink_count(struct shrinker *shrink, struct shrink_control *sc) + return count; + } + +-static void ttm_dma_pool_mm_shrink_init(struct ttm_pool_manager *manager) ++static int ttm_dma_pool_mm_shrink_init(struct ttm_pool_manager *manager) + { + manager->mm_shrink.count_objects = ttm_dma_pool_shrink_count; + manager->mm_shrink.scan_objects = &ttm_dma_pool_shrink_scan; + manager->mm_shrink.seeks = 1; +- register_shrinker(&manager->mm_shrink); ++ return register_shrinker(&manager->mm_shrink); + } + + static void ttm_dma_pool_mm_shrink_fini(struct ttm_pool_manager *manager) +@@ -1082,7 +1200,7 @@ static void ttm_dma_pool_mm_shrink_fini(struct ttm_pool_manager *manager) + + int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) + { +- int ret = -ENOMEM; ++ int ret; + + WARN_ON(_manager); + +@@ -1090,7 +1208,7 @@ int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) + + _manager = kzalloc(sizeof(*_manager), GFP_KERNEL); + if (!_manager) +- goto err; ++ return -ENOMEM; + + mutex_init(&_manager->lock); + INIT_LIST_HEAD(&_manager->pools); +@@ -1102,13 +1220,17 @@ int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) + /* This takes care of auto-freeing the _manager */ + ret = kobject_init_and_add(&_manager->kobj, &ttm_pool_kobj_type, + &glob->kobj, "dma_pool"); +- if (unlikely(ret != 0)) { +- kobject_put(&_manager->kobj); +- goto err; +- } +- ttm_dma_pool_mm_shrink_init(_manager); ++ if (unlikely(ret != 0)) ++ goto error; ++ ++ ret = ttm_dma_pool_mm_shrink_init(_manager); ++ if (unlikely(ret != 0)) ++ goto error; + return 0; +-err: ++ ++error: ++ kobject_put(&_manager->kobj); ++ _manager = NULL; + return ret; + } + +@@ -1134,15 +1256,12 @@ int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data) + { + struct device_pools *p; + struct dma_pool *pool = NULL; +- char *h[] = {"pool", "refills", "pages freed", "inuse", "available", +- "name", "virt", "busaddr"}; + + if (!_manager) { + seq_printf(m, "No pool allocator running.\n"); + return 0; + } +- seq_printf(m, "%13s %12s %13s %8s %8s %8s\n", +- h[0], h[1], h[2], h[3], h[4], h[5]); ++ seq_printf(m, " pool refills pages freed inuse available name\n"); + mutex_lock(&_manager->lock); + list_for_each_entry(p, &_manager->pools, pools) { + struct device *dev = p->dev; +diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c +index 522a80d..a9acda4 100644 +--- a/drivers/gpu/drm/ttm/ttm_tt.c ++++ b/drivers/gpu/drm/ttm/ttm_tt.c +@@ -83,16 +83,13 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc) + if (unlikely(bo->ttm == NULL)) + return -ENOMEM; + +- if (bo->type == ttm_bo_type_sg) +- bo->ttm->sg = bo->sg; +- +- return 0; ++ return 0; + } + + /** + * Allocates storage for pointers to the pages that back the ttm. + */ +-static void ttm_tt_alloc_page_directory(struct ttm_tt *ttm) ++static int ttm_tt_alloc_page_directory(struct ttm_tt *ttm) + { + ttm->pages = kvmalloc_array(ttm->num_pages, sizeof(void*), + GFP_KERNEL | __GFP_ZERO); +@@ -105,6 +102,7 @@ static int ttm_dma_tt_alloc_page_directory(struct ttm_dma_tt *ttm) + sizeof(*ttm->dma_address), + GFP_KERNEL | __GFP_ZERO); + ttm->dma_address = (void *) (ttm->ttm.pages + ttm->ttm.num_pages); ++ return 0; + } + + static int ttm_sg_tt_alloc_page_directory(struct ttm_dma_tt *ttm) +@@ -246,6 +244,7 @@ void ttm_tt_init_fields(struct ttm_tt *ttm, struct ttm_buffer_object *bo, + ttm->page_flags = page_flags; + ttm->state = tt_unpopulated; + ttm->swap_storage = NULL; ++ ttm->sg = bo->sg; + } + + int ttm_tt_init(struct ttm_tt *ttm, struct ttm_buffer_object *bo, +@@ -253,8 +252,7 @@ int ttm_tt_init(struct ttm_tt *ttm, struct ttm_buffer_object *bo, + { + ttm_tt_init_fields(ttm, bo, page_flags); + +- ttm_tt_alloc_page_directory(ttm); +- if (!ttm->pages) { ++ if (ttm_tt_alloc_page_directory(ttm)) { + ttm_tt_destroy(ttm); + pr_err("Failed allocating page table\n"); + return -ENOMEM; +@@ -278,8 +276,7 @@ int ttm_dma_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_buffer_object *bo, + ttm_tt_init_fields(ttm, bo, page_flags); + + INIT_LIST_HEAD(&ttm_dma->pages_list); +- ttm_dma_tt_alloc_page_directory(ttm_dma); +- if (!ttm->pages) { ++ if (ttm_dma_tt_alloc_page_directory(ttm_dma)) { + ttm_tt_destroy(ttm); + pr_err("Failed allocating page table\n"); + return -ENOMEM; +@@ -314,7 +311,10 @@ void ttm_dma_tt_fini(struct ttm_dma_tt *ttm_dma) + { + struct ttm_tt *ttm = &ttm_dma->ttm; + ++ if (ttm->pages) + kvfree(ttm->pages); ++ else ++ kvfree(ttm_dma->dma_address); + ttm->pages = NULL; + ttm_dma->dma_address = NULL; + } +@@ -342,7 +342,7 @@ int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem, + if (ttm->state == tt_bound) + return 0; + +- ret = ttm->bdev->driver->ttm_tt_populate(ttm, ctx); ++ ret = ttm_tt_populate(ttm, ctx); + if (ret) + return ret; + +@@ -371,7 +371,11 @@ int ttm_tt_swapin(struct ttm_tt *ttm) + swap_space = swap_storage->f_mapping; + + for (i = 0; i < ttm->num_pages; ++i) { +- from_page = shmem_read_mapping_page(swap_space, i); ++ gfp_t gfp_mask = mapping_gfp_mask(swap_space); ++ ++ gfp_mask |= (ttm->page_flags & TTM_PAGE_FLAG_NO_RETRY ? __GFP_RETRY_MAYFAIL : 0); ++ from_page = shmem_read_mapping_page_gfp(swap_space, i, gfp_mask); ++ + if (IS_ERR(from_page)) { + ret = PTR_ERR(from_page); + goto out_err; +@@ -414,16 +418,22 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage) + pr_err("Failed allocating swap storage\n"); + return PTR_ERR(swap_storage); + } +- } else ++ } else { + swap_storage = persistent_swap_storage; ++ } + + swap_space = swap_storage->f_mapping; + + for (i = 0; i < ttm->num_pages; ++i) { ++ gfp_t gfp_mask = mapping_gfp_mask(swap_space); ++ ++ gfp_mask |= (ttm->page_flags & TTM_PAGE_FLAG_NO_RETRY ? __GFP_RETRY_MAYFAIL : 0); ++ + from_page = ttm->pages[i]; + if (unlikely(from_page == NULL)) + continue; +- to_page = shmem_read_mapping_page(swap_space, i); ++ ++ to_page = shmem_read_mapping_page_gfp(swap_space, i, gfp_mask); + if (IS_ERR(to_page)) { + ret = PTR_ERR(to_page); + goto out_err; +@@ -448,6 +458,33 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage) + return ret; + } + ++static void ttm_tt_add_mapping(struct ttm_tt *ttm) ++{ ++ pgoff_t i; ++ ++ if (ttm->page_flags & TTM_PAGE_FLAG_SG) ++ return; ++ ++ for (i = 0; i < ttm->num_pages; ++i) ++ ttm->pages[i]->mapping = ttm->bdev->dev_mapping; ++} ++ ++int ttm_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx) ++{ ++ int ret; ++ ++ if (ttm->state != tt_unpopulated) ++ return 0; ++ ++ if (ttm->bdev->driver->ttm_tt_populate) ++ ret = ttm->bdev->driver->ttm_tt_populate(ttm, ctx); ++ else ++ ret = ttm_pool_populate(ttm, ctx); ++ if (!ret) ++ ttm_tt_add_mapping(ttm); ++ return ret; ++} ++ + static void ttm_tt_clear_mapping(struct ttm_tt *ttm) + { + pgoff_t i; +@@ -468,5 +505,8 @@ void ttm_tt_unpopulate(struct ttm_tt *ttm) + return; + + ttm_tt_clear_mapping(ttm); +- ttm->bdev->driver->ttm_tt_unpopulate(ttm); ++ if (ttm->bdev->driver->ttm_tt_unpopulate) ++ ttm->bdev->driver->ttm_tt_unpopulate(ttm); ++ else ++ ttm_pool_unpopulate(ttm); + } +diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c +index a9f2fb0..3848903 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_ttm.c ++++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c +@@ -428,7 +428,6 @@ static struct ttm_bo_driver virtio_gpu_bo_driver = { + .verify_access = &virtio_gpu_verify_access, + .io_mem_reserve = &virtio_gpu_ttm_io_mem_reserve, + .io_mem_free = &virtio_gpu_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + .move_notify = &virtio_gpu_bo_move_notify, + .swap_notify = &virtio_gpu_bo_swap_notify, + }; +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +index bab10e3..7177eec 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +@@ -860,5 +860,4 @@ struct ttm_bo_driver vmw_bo_driver = { + .fault_reserve_notify = &vmw_ttm_fault_reserve_notify, + .io_mem_reserve = &vmw_ttm_io_mem_reserve, + .io_mem_free = &vmw_ttm_io_mem_free, +- .io_mem_pfn = ttm_bo_default_io_mem_pfn, + }; +diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h +index 2f71269..1822c4e 100644 +--- a/include/drm/ttm/ttm_bo_driver.h ++++ b/include/drm/ttm/ttm_bo_driver.h +@@ -609,6 +609,17 @@ ttm_flag_masked(uint32_t *old, uint32_t new, uint32_t mask) + } + + /** ++ * ttm_tt_create ++ * ++ * @bo: pointer to a struct ttm_buffer_object ++ * @zero_alloc: true if allocated pages needs to be zeroed ++ * ++ * Make sure we have a TTM structure allocated for the given BO. ++ * No pages are actually allocated. ++ */ ++int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc); ++ ++/** + * ttm_tt_init + * + * @ttm: The struct ttm_tt. +@@ -647,8 +658,8 @@ extern void ttm_dma_tt_fini(struct ttm_dma_tt *ttm_dma); + * + * Bind the pages of @ttm to an aperture location identified by @bo_mem + */ +-extern int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem); +- ++extern int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem, ++ struct ttm_operation_ctx *ctx); + /** + * ttm_ttm_destroy: + * +@@ -694,6 +705,15 @@ extern int ttm_tt_swapout(struct ttm_tt *ttm, + struct file *persistent_swap_storage); + + /** ++ * ttm_tt_populate - allocate pages for a ttm ++ * ++ * @ttm: Pointer to the ttm_tt structure ++ * ++ * Calls the driver method to allocate pages for a ttm ++ */ ++extern int ttm_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx); ++ ++/** + * ttm_tt_unpopulate - free pages from a ttm + * + * @ttm: Pointer to the ttm_tt structure +@@ -823,24 +843,24 @@ static inline int __ttm_bo_reserve(struct ttm_buffer_object *bo, + bool interruptible, bool no_wait, + struct ww_acquire_ctx *ticket) + { +- int ret = 0; +- +- if (no_wait) { +- bool success; +- if (WARN_ON(ticket)) +- return -EBUSY; +- +- success = ww_mutex_trylock(&bo->resv->lock); +- return success ? 0 : -EBUSY; +- } +- +- if (interruptible) +- ret = ww_mutex_lock_interruptible(&bo->resv->lock, ticket); +- else +- ret = ww_mutex_lock(&bo->resv->lock, ticket); +- if (ret == -EINTR) +- return -ERESTARTSYS; +- return ret; ++ int ret = 0; ++ ++ if (no_wait) { ++ bool success; ++ if (WARN_ON(ticket)) ++ return -EBUSY; ++ ++ success = reservation_object_trylock(bo->resv); ++ return success ? 0 : -EBUSY; ++ } ++ ++ if (interruptible) ++ ret = reservation_object_lock_interruptible(bo->resv, ticket); ++ else ++ ret = reservation_object_lock(bo->resv, ticket); ++ if (ret == -EINTR) ++ return -ERESTARTSYS; ++ return ret; + } + + /** +@@ -935,18 +955,6 @@ static inline int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + } + + /** +- * __ttm_bo_unreserve +- * @bo: A pointer to a struct ttm_buffer_object. +- * +- * Unreserve a previous reservation of @bo where the buffer object is +- * already on lru lists. +- */ +-static inline void __ttm_bo_unreserve(struct ttm_buffer_object *bo) +-{ +- ww_mutex_unlock(&bo->resv->lock); +-} +- +-/** + * ttm_bo_unreserve + * + * @bo: A pointer to a struct ttm_buffer_object. +@@ -955,13 +963,12 @@ static inline void __ttm_bo_unreserve(struct ttm_buffer_object *bo) + */ + static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo) + { +- if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { +- spin_lock(&bo->bdev->glob->lru_lock); +- ttm_bo_add_to_lru(bo); +- spin_unlock(&bo->glob->lru_lock); +- spin_unlock(&bo->bdev->glob->lru_lock); +- } +- __ttm_bo_unreserve(bo); ++ if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { ++ spin_lock(&bo->bdev->glob->lru_lock); ++ ttm_bo_add_to_lru(bo); ++ spin_unlock(&bo->bdev->glob->lru_lock); ++ } ++ reservation_object_unlock(bo->resv); + } + + /** +@@ -1113,7 +1120,7 @@ extern const struct ttm_mem_type_manager_func ttm_bo_manager_func; + struct ttm_tt *ttm_agp_tt_create(struct ttm_buffer_object *bo, + struct agp_bridge_data *bridge, + uint32_t page_flags); +-int ttm_agp_tt_populate(struct ttm_tt *ttm); ++int ttm_agp_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx); + void ttm_agp_tt_unpopulate(struct ttm_tt *ttm); + #endif + +diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h +index c8885bc9..bc8b22e 100644 +--- a/include/drm/ttm/ttm_memory.h ++++ b/include/drm/ttm/ttm_memory.h +@@ -38,20 +38,6 @@ + #include "ttm_bo_api.h" + + /** +- * struct ttm_mem_shrink - callback to shrink TTM memory usage. +- * +- * @do_shrink: The callback function. +- * +- * Arguments to the do_shrink functions are intended to be passed using +- * inheritance. That is, the argument class derives from struct ttm_mem_shrink, +- * and can be accessed using container_of(). +- */ +- +-struct ttm_mem_shrink { +- int (*do_shrink) (struct ttm_mem_shrink *); +-}; +- +-/** + * struct ttm_mem_global - Global memory accounting structure. + * + * @shrink: A single callback to shrink TTM memory usage. Extend this +@@ -94,59 +80,6 @@ struct ttm_mem_global { + #endif + }; + +-/** +- * ttm_mem_init_shrink - initialize a struct ttm_mem_shrink object +- * +- * @shrink: The object to initialize. +- * @func: The callback function. +- */ +- +-static inline void ttm_mem_init_shrink(struct ttm_mem_shrink *shrink, +- int (*func) (struct ttm_mem_shrink *)) +-{ +- shrink->do_shrink = func; +-} +- +-/** +- * ttm_mem_register_shrink - register a struct ttm_mem_shrink object. +- * +- * @glob: The struct ttm_mem_global object to register with. +- * @shrink: An initialized struct ttm_mem_shrink object to register. +- * +- * Returns: +- * -EBUSY: There's already a callback registered. (May change). +- */ +- +-static inline int ttm_mem_register_shrink(struct ttm_mem_global *glob, +- struct ttm_mem_shrink *shrink) +-{ +- spin_lock(&glob->lock); +- if (glob->shrink != NULL) { +- spin_unlock(&glob->lock); +- return -EBUSY; +- } +- glob->shrink = shrink; +- spin_unlock(&glob->lock); +- return 0; +-} +- +-/** +- * ttm_mem_unregister_shrink - unregister a struct ttm_mem_shrink object. +- * +- * @glob: The struct ttm_mem_global object to unregister from. +- * @shrink: A previously registert struct ttm_mem_shrink object. +- * +- */ +- +-static inline void ttm_mem_unregister_shrink(struct ttm_mem_global *glob, +- struct ttm_mem_shrink *shrink) +-{ +- spin_lock(&glob->lock); +- BUG_ON(glob->shrink != shrink); +- glob->shrink = NULL; +- spin_unlock(&glob->lock); +-} +- + extern int ttm_mem_global_init(struct ttm_mem_global *glob); + extern void ttm_mem_global_release(struct ttm_mem_global *glob); + extern int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, +@@ -157,7 +90,7 @@ extern int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, + struct page *page, uint64_t size, + struct ttm_operation_ctx *ctx); + extern void ttm_mem_global_free_page(struct ttm_mem_global *glob, +- struct page *page); ++ struct page *page, uint64_t size); + extern size_t ttm_round_pot(size_t size); + extern uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob); + extern bool ttm_check_under_lowerlimit(struct ttm_mem_global *glob, +diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h +index 80aa53c..606a3ab 100644 +--- a/include/drm/ttm/ttm_page_alloc.h ++++ b/include/drm/ttm/ttm_page_alloc.h +@@ -58,7 +58,7 @@ extern int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx); + */ + extern void ttm_pool_unpopulate(struct ttm_tt *ttm); + +-extern +int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt, ++extern int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt, + struct ttm_operation_ctx *ctx); + + /** +@@ -92,11 +92,6 @@ extern int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev, + extern void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev); + + /** +- * Populates and DMA maps pages to fullfil a ttm_dma_populate() request +- */ +-int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt); +- +-/** + * Unpopulates and DMA unmaps pages as part of a + * ttm_dma_unpopulate() request */ + void ttm_unmap_and_unpopulate_pages(struct device *dev, struct ttm_dma_tt *tt); +diff --git a/include/linux/reservation.h b/include/linux/reservation.h +index 21fc84d..eb444ff 100644 +--- a/include/linux/reservation.h ++++ b/include/linux/reservation.h +@@ -167,6 +167,28 @@ reservation_object_lock(struct reservation_object *obj, + } + + /** ++ * reservation_object_lock_interruptible - lock the reservation object ++ * @obj: the reservation object ++ * @ctx: the locking context ++ * ++ * Locks the reservation object interruptible for exclusive access and ++ * modification. Note, that the lock is only against other writers, readers ++ * will run concurrently with a writer under RCU. The seqlock is used to ++ * notify readers if they overlap with a writer. ++ * ++ * As the reservation object may be locked by multiple parties in an ++ * undefined order, a #ww_acquire_ctx is passed to unwind if a cycle ++ * is detected. See ww_mutex_lock() and ww_acquire_init(). A reservation ++ * object may be locked by itself by passing NULL as @ctx. ++ */ ++static inline int ++reservation_object_lock_interruptible(struct reservation_object *obj, ++ struct ww_acquire_ctx *ctx) ++{ ++ return ww_mutex_lock_interruptible(&obj->lock, ctx); ++} ++ ++/** + * reservation_object_trylock - trylock the reservation object + * @obj: the reservation object + * +-- +2.7.4 + |