diff options
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_memory.c | 93 | ||||
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_page_alloc.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 3 | ||||
-rw-r--r-- | include/drm/ttm/ttm_memory.h | 5 |
4 files changed, 104 insertions, 0 deletions
diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c index aa0c38136958..27856c55dc84 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, @@ -375,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; @@ -469,6 +533,35 @@ 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) diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index 2c28c4568c5f..f0481b7b60c5 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -1100,6 +1100,9 @@ int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx) if (ttm->state != tt_unpopulated) return 0; + 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)) { diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c index 3b4c97011b5c..8a25d1974385 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c @@ -940,6 +940,9 @@ int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev, 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; diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h index 8936285b6543..737b5fed8003 100644 --- a/include/drm/ttm/ttm_memory.h +++ b/include/drm/ttm/ttm_memory.h @@ -49,6 +49,8 @@ * @work: The workqueue callback for the shrink queue. * @lock: Lock to protect the @shrink - and the memory accounting members, * that is, essentially the whole structure with some exceptions. + * @lower_mem_limit: include lower limit of swap space and lower limit of + * system memory. * @zones: Array of pointers to accounting zones. * @num_zones: Number of populated entries in the @zones array. * @zone_kernel: Pointer to the kernel zone. @@ -67,6 +69,7 @@ struct ttm_mem_global { struct workqueue_struct *swap_queue; struct work_struct work; spinlock_t lock; + uint64_t lower_mem_limit; struct ttm_mem_zone *zones[TTM_MEM_MAX_ZONES]; unsigned int num_zones; struct ttm_mem_zone *zone_kernel; @@ -90,4 +93,6 @@ extern void ttm_mem_global_free_page(struct ttm_mem_global *glob, 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, + uint64_t num_pages, struct ttm_operation_ctx *ctx); #endif |