15718399fSFrançois Tigeot /************************************************************************** 25718399fSFrançois Tigeot * 35718399fSFrançois Tigeot * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA 45718399fSFrançois Tigeot * All Rights Reserved. 55718399fSFrançois Tigeot * 65718399fSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 75718399fSFrançois Tigeot * copy of this software and associated documentation files (the 85718399fSFrançois Tigeot * "Software"), to deal in the Software without restriction, including 95718399fSFrançois Tigeot * without limitation the rights to use, copy, modify, merge, publish, 105718399fSFrançois Tigeot * distribute, sub license, and/or sell copies of the Software, and to 115718399fSFrançois Tigeot * permit persons to whom the Software is furnished to do so, subject to 125718399fSFrançois Tigeot * the following conditions: 135718399fSFrançois Tigeot * 145718399fSFrançois Tigeot * The above copyright notice and this permission notice (including the 155718399fSFrançois Tigeot * next paragraph) shall be included in all copies or substantial portions 165718399fSFrançois Tigeot * of the Software. 175718399fSFrançois Tigeot * 185718399fSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 195718399fSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 205718399fSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 215718399fSFrançois Tigeot * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 225718399fSFrançois Tigeot * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 235718399fSFrançois Tigeot * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 245718399fSFrançois Tigeot * USE OR OTHER DEALINGS IN THE SOFTWARE. 255718399fSFrançois Tigeot * 265718399fSFrançois Tigeot **************************************************************************/ 275718399fSFrançois Tigeot 280bece63dSImre Vadasz #define pr_fmt(fmt) "[TTM] " fmt 290bece63dSImre Vadasz 30216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_memory.h> 31216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_module.h> 32216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_page_alloc.h> 3360b00aa3SFrançois Tigeot #include <linux/spinlock.h> 3460b00aa3SFrançois Tigeot #include <linux/sched.h> 3560b00aa3SFrançois Tigeot #include <linux/wait.h> 3660b00aa3SFrançois Tigeot #include <linux/mm.h> 3760b00aa3SFrançois Tigeot #include <linux/module.h> 3860b00aa3SFrançois Tigeot #include <linux/slab.h> 395718399fSFrançois Tigeot 405718399fSFrançois Tigeot #define TTM_MEMORY_ALLOC_RETRIES 4 415718399fSFrançois Tigeot 425718399fSFrançois Tigeot struct ttm_mem_zone { 433a2096e8SFrançois Tigeot struct kobject kobj; 445718399fSFrançois Tigeot struct ttm_mem_global *glob; 455718399fSFrançois Tigeot const char *name; 465718399fSFrançois Tigeot uint64_t zone_mem; 475718399fSFrançois Tigeot uint64_t emer_mem; 485718399fSFrançois Tigeot uint64_t max_mem; 495718399fSFrançois Tigeot uint64_t swap_limit; 505718399fSFrançois Tigeot uint64_t used_mem; 515718399fSFrançois Tigeot }; 525718399fSFrançois Tigeot 533a2096e8SFrançois Tigeot static struct attribute ttm_mem_sys = { 543a2096e8SFrançois Tigeot .name = "zone_memory", 553a2096e8SFrançois Tigeot .mode = S_IRUGO 563a2096e8SFrançois Tigeot }; 573a2096e8SFrançois Tigeot static struct attribute ttm_mem_emer = { 583a2096e8SFrançois Tigeot .name = "emergency_memory", 593a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 603a2096e8SFrançois Tigeot }; 613a2096e8SFrançois Tigeot static struct attribute ttm_mem_max = { 623a2096e8SFrançois Tigeot .name = "available_memory", 633a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 643a2096e8SFrançois Tigeot }; 653a2096e8SFrançois Tigeot static struct attribute ttm_mem_swap = { 663a2096e8SFrançois Tigeot .name = "swap_limit", 673a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 683a2096e8SFrançois Tigeot }; 693a2096e8SFrançois Tigeot static struct attribute ttm_mem_used = { 703a2096e8SFrançois Tigeot .name = "used_memory", 713a2096e8SFrançois Tigeot .mode = S_IRUGO 723a2096e8SFrançois Tigeot }; 733a2096e8SFrançois Tigeot 743a2096e8SFrançois Tigeot static void ttm_mem_zone_kobj_release(struct kobject *kobj) 755718399fSFrançois Tigeot { 763a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 773a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_zone, kobj); 785718399fSFrançois Tigeot 790bece63dSImre Vadasz pr_info("Zone %7s: Used memory at exit: %llu kiB\n", 805718399fSFrançois Tigeot zone->name, (unsigned long long)zone->used_mem >> 10); 81175896dfSzrj kfree(zone); 825718399fSFrançois Tigeot } 835718399fSFrançois Tigeot 843a2096e8SFrançois Tigeot static ssize_t ttm_mem_zone_show(struct kobject *kobj, 855718399fSFrançois Tigeot struct attribute *attr, 865718399fSFrançois Tigeot char *buffer) 875718399fSFrançois Tigeot { 883a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 893a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_zone, kobj); 905718399fSFrançois Tigeot uint64_t val = 0; 915718399fSFrançois Tigeot 92ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_EXCLUSIVE); 935718399fSFrançois Tigeot if (attr == &ttm_mem_sys) 945718399fSFrançois Tigeot val = zone->zone_mem; 955718399fSFrançois Tigeot else if (attr == &ttm_mem_emer) 965718399fSFrançois Tigeot val = zone->emer_mem; 975718399fSFrançois Tigeot else if (attr == &ttm_mem_max) 985718399fSFrançois Tigeot val = zone->max_mem; 995718399fSFrançois Tigeot else if (attr == &ttm_mem_swap) 1005718399fSFrançois Tigeot val = zone->swap_limit; 1015718399fSFrançois Tigeot else if (attr == &ttm_mem_used) 1025718399fSFrançois Tigeot val = zone->used_mem; 103ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_RELEASE); 1045718399fSFrançois Tigeot 1053a2096e8SFrançois Tigeot return ksnprintf(buffer, PAGE_SIZE, "%llu\n", 1065718399fSFrançois Tigeot (unsigned long long) val >> 10); 1075718399fSFrançois Tigeot } 1085718399fSFrançois Tigeot 1095718399fSFrançois Tigeot static void ttm_check_swapping(struct ttm_mem_global *glob); 1105718399fSFrançois Tigeot 1113a2096e8SFrançois Tigeot static ssize_t ttm_mem_zone_store(struct kobject *kobj, 1125718399fSFrançois Tigeot struct attribute *attr, 1135718399fSFrançois Tigeot const char *buffer, 1145718399fSFrançois Tigeot size_t size) 1155718399fSFrançois Tigeot { 1163a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 1173a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_zone, kobj); 1185718399fSFrançois Tigeot int chars; 1195718399fSFrançois Tigeot unsigned long val; 1205718399fSFrançois Tigeot uint64_t val64; 1215718399fSFrançois Tigeot 1223a2096e8SFrançois Tigeot chars = ksscanf(buffer, "%lu", &val); 1235718399fSFrançois Tigeot if (chars == 0) 1245718399fSFrançois Tigeot return size; 1255718399fSFrançois Tigeot 1265718399fSFrançois Tigeot val64 = val; 1275718399fSFrançois Tigeot val64 <<= 10; 1285718399fSFrançois Tigeot 129ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_EXCLUSIVE); 1305718399fSFrançois Tigeot if (val64 > zone->zone_mem) 1315718399fSFrançois Tigeot val64 = zone->zone_mem; 1325718399fSFrançois Tigeot if (attr == &ttm_mem_emer) { 1335718399fSFrançois Tigeot zone->emer_mem = val64; 1345718399fSFrançois Tigeot if (zone->max_mem > val64) 1355718399fSFrançois Tigeot zone->max_mem = val64; 1365718399fSFrançois Tigeot } else if (attr == &ttm_mem_max) { 1375718399fSFrançois Tigeot zone->max_mem = val64; 1385718399fSFrançois Tigeot if (zone->emer_mem < val64) 1395718399fSFrançois Tigeot zone->emer_mem = val64; 1405718399fSFrançois Tigeot } else if (attr == &ttm_mem_swap) 1415718399fSFrançois Tigeot zone->swap_limit = val64; 142ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_RELEASE); 1435718399fSFrançois Tigeot 1445718399fSFrançois Tigeot ttm_check_swapping(zone->glob); 1455718399fSFrançois Tigeot 1465718399fSFrançois Tigeot return size; 1475718399fSFrançois Tigeot } 1485718399fSFrançois Tigeot 1493a2096e8SFrançois Tigeot static struct attribute *ttm_mem_zone_attrs[] = { 1503a2096e8SFrançois Tigeot &ttm_mem_sys, 1513a2096e8SFrançois Tigeot &ttm_mem_emer, 1523a2096e8SFrançois Tigeot &ttm_mem_max, 1533a2096e8SFrançois Tigeot &ttm_mem_swap, 1543a2096e8SFrançois Tigeot &ttm_mem_used, 1553a2096e8SFrançois Tigeot NULL 1563a2096e8SFrançois Tigeot }; 1573a2096e8SFrançois Tigeot 1583a2096e8SFrançois Tigeot static const struct sysfs_ops ttm_mem_zone_ops = { 1593a2096e8SFrançois Tigeot .show = &ttm_mem_zone_show, 1603a2096e8SFrançois Tigeot .store = &ttm_mem_zone_store 1613a2096e8SFrançois Tigeot }; 1623a2096e8SFrançois Tigeot 1633a2096e8SFrançois Tigeot static struct kobj_type ttm_mem_zone_kobj_type = { 1643a2096e8SFrançois Tigeot .release = &ttm_mem_zone_kobj_release, 1653a2096e8SFrançois Tigeot .sysfs_ops = &ttm_mem_zone_ops, 1663a2096e8SFrançois Tigeot .default_attrs = ttm_mem_zone_attrs, 1673a2096e8SFrançois Tigeot }; 1683a2096e8SFrançois Tigeot 1693a2096e8SFrançois Tigeot static void ttm_mem_global_kobj_release(struct kobject *kobj) 1705718399fSFrançois Tigeot { 1713a2096e8SFrançois Tigeot struct ttm_mem_global *glob = 1723a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_global, kobj); 1733a2096e8SFrançois Tigeot 1743a2096e8SFrançois Tigeot kfree(glob); 1755718399fSFrançois Tigeot } 1765718399fSFrançois Tigeot 1773a2096e8SFrançois Tigeot static struct kobj_type ttm_mem_glob_kobj_type = { 1783a2096e8SFrançois Tigeot .release = &ttm_mem_global_kobj_release, 1793a2096e8SFrançois Tigeot }; 1803a2096e8SFrançois Tigeot 1815718399fSFrançois Tigeot static bool ttm_zones_above_swap_target(struct ttm_mem_global *glob, 1825718399fSFrançois Tigeot bool from_wq, uint64_t extra) 1835718399fSFrançois Tigeot { 1845718399fSFrançois Tigeot unsigned int i; 1855718399fSFrançois Tigeot struct ttm_mem_zone *zone; 1865718399fSFrançois Tigeot uint64_t target; 1875718399fSFrançois Tigeot 1885718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 1895718399fSFrançois Tigeot zone = glob->zones[i]; 1905718399fSFrançois Tigeot 1915718399fSFrançois Tigeot if (from_wq) 1925718399fSFrançois Tigeot target = zone->swap_limit; 1935718399fSFrançois Tigeot else if (priv_check(curthread, PRIV_VM_MLOCK) == 0) 1945718399fSFrançois Tigeot target = zone->emer_mem; 1955718399fSFrançois Tigeot else 1965718399fSFrançois Tigeot target = zone->max_mem; 1975718399fSFrançois Tigeot 1985718399fSFrançois Tigeot target = (extra > target) ? 0ULL : target; 1995718399fSFrançois Tigeot 2005718399fSFrançois Tigeot if (zone->used_mem > target) 2015718399fSFrançois Tigeot return true; 2025718399fSFrançois Tigeot } 2035718399fSFrançois Tigeot return false; 2045718399fSFrançois Tigeot } 2055718399fSFrançois Tigeot 2065718399fSFrançois Tigeot /** 2075718399fSFrançois Tigeot * At this point we only support a single shrink callback. 2085718399fSFrançois Tigeot * Extend this if needed, perhaps using a linked list of callbacks. 2095718399fSFrançois Tigeot * Note that this function is reentrant: 2105718399fSFrançois Tigeot * many threads may try to swap out at any given time. 2115718399fSFrançois Tigeot */ 2125718399fSFrançois Tigeot 2135718399fSFrançois Tigeot static void ttm_shrink(struct ttm_mem_global *glob, bool from_wq, 214*932d855eSSergey Zigachev uint64_t extra, struct ttm_operation_ctx *ctx) 2155718399fSFrançois Tigeot { 2165718399fSFrançois Tigeot int ret; 2175718399fSFrançois Tigeot 218ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 2195718399fSFrançois Tigeot 2205718399fSFrançois Tigeot while (ttm_zones_above_swap_target(glob, from_wq, extra)) { 221ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 222*932d855eSSergey Zigachev ret = ttm_bo_swapout(glob->bo_glob, ctx); 223ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 2245718399fSFrançois Tigeot if (unlikely(ret != 0)) 225*932d855eSSergey Zigachev break; 2265718399fSFrançois Tigeot } 227*932d855eSSergey Zigachev 228ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 2295718399fSFrançois Tigeot } 2305718399fSFrançois Tigeot 23160b00aa3SFrançois Tigeot static void ttm_shrink_work(struct work_struct *work) 2325718399fSFrançois Tigeot { 233*932d855eSSergey Zigachev struct ttm_operation_ctx ctx = { 234*932d855eSSergey Zigachev .interruptible = false, 235*932d855eSSergey Zigachev .no_wait_gpu = false 236*932d855eSSergey Zigachev }; 23760b00aa3SFrançois Tigeot struct ttm_mem_global *glob = 23860b00aa3SFrançois Tigeot container_of(work, struct ttm_mem_global, work); 2395718399fSFrançois Tigeot 240*932d855eSSergey Zigachev ttm_shrink(glob, true, 0ULL, &ctx); 2415718399fSFrançois Tigeot } 2425718399fSFrançois Tigeot 2435718399fSFrançois Tigeot static int ttm_mem_init_kernel_zone(struct ttm_mem_global *glob, 2445718399fSFrançois Tigeot uint64_t mem) 2455718399fSFrançois Tigeot { 2463a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL); 2473a2096e8SFrançois Tigeot int ret; 2485718399fSFrançois Tigeot 2495718399fSFrançois Tigeot zone->name = "kernel"; 2505718399fSFrançois Tigeot zone->zone_mem = mem; 2515718399fSFrançois Tigeot zone->max_mem = mem >> 1; 2525718399fSFrançois Tigeot zone->emer_mem = (mem >> 1) + (mem >> 2); 2535718399fSFrançois Tigeot zone->swap_limit = zone->max_mem - (mem >> 3); 2545718399fSFrançois Tigeot zone->used_mem = 0; 2555718399fSFrançois Tigeot zone->glob = glob; 2565718399fSFrançois Tigeot glob->zone_kernel = zone; 2573a2096e8SFrançois Tigeot ret = kobject_init_and_add( 2583a2096e8SFrançois Tigeot &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, zone->name); 2593a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 2603a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 2613a2096e8SFrançois Tigeot return ret; 2623a2096e8SFrançois Tigeot } 2635718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 2645718399fSFrançois Tigeot return 0; 2655718399fSFrançois Tigeot } 2665718399fSFrançois Tigeot 2673a2096e8SFrançois Tigeot #ifdef CONFIG_HIGHMEM 2683a2096e8SFrançois Tigeot #else 2695718399fSFrançois Tigeot static int ttm_mem_init_dma32_zone(struct ttm_mem_global *glob, 2705718399fSFrançois Tigeot uint64_t mem) 2715718399fSFrançois Tigeot { 2723a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL); 2733a2096e8SFrançois Tigeot int ret; 2745718399fSFrançois Tigeot 2755718399fSFrançois Tigeot /** 2765718399fSFrançois Tigeot * No special dma32 zone needed. 2775718399fSFrançois Tigeot */ 2785718399fSFrançois Tigeot 279f6201ebfSMatthew Dillon if ((physmem * PAGE_SIZE) <= ((uint64_t) 1ULL << 32)) { 280175896dfSzrj kfree(zone); 2815718399fSFrançois Tigeot return 0; 2825718399fSFrançois Tigeot } 2835718399fSFrançois Tigeot 2845718399fSFrançois Tigeot /* 2855718399fSFrançois Tigeot * Limit max dma32 memory to 4GB for now 2865718399fSFrançois Tigeot * until we can figure out how big this 2875718399fSFrançois Tigeot * zone really is. 2885718399fSFrançois Tigeot */ 289f6201ebfSMatthew Dillon if (mem > ((uint64_t) 1ULL << 32)) 2905718399fSFrançois Tigeot mem = ((uint64_t) 1ULL << 32); 291f6201ebfSMatthew Dillon 2925718399fSFrançois Tigeot zone->name = "dma32"; 2935718399fSFrançois Tigeot zone->zone_mem = mem; 2945718399fSFrançois Tigeot zone->max_mem = mem >> 1; 2955718399fSFrançois Tigeot zone->emer_mem = (mem >> 1) + (mem >> 2); 2965718399fSFrançois Tigeot zone->swap_limit = zone->max_mem - (mem >> 3); 2975718399fSFrançois Tigeot zone->used_mem = 0; 2985718399fSFrançois Tigeot zone->glob = glob; 2995718399fSFrançois Tigeot glob->zone_dma32 = zone; 3003a2096e8SFrançois Tigeot ret = kobject_init_and_add( 30160b00aa3SFrançois Tigeot &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, zone->name); 3023a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 3033a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 3043a2096e8SFrançois Tigeot return ret; 3053a2096e8SFrançois Tigeot } 3065718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 3075718399fSFrançois Tigeot return 0; 3085718399fSFrançois Tigeot } 3093a2096e8SFrançois Tigeot #endif 3105718399fSFrançois Tigeot 3115718399fSFrançois Tigeot int ttm_mem_global_init(struct ttm_mem_global *glob) 3125718399fSFrançois Tigeot { 3135718399fSFrançois Tigeot u_int64_t mem; 3145718399fSFrançois Tigeot int ret; 3155718399fSFrançois Tigeot int i; 3165718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3175718399fSFrançois Tigeot 3189a49c39cSFrançois Tigeot lockinit(&glob->lock, "ttmemglob", 0, 0); 31960b00aa3SFrançois Tigeot glob->swap_queue = create_singlethread_workqueue("ttm_swap"); 32060b00aa3SFrançois Tigeot INIT_WORK(&glob->work, ttm_shrink_work); 3213a2096e8SFrançois Tigeot ret = kobject_init_and_add( 3223a2096e8SFrançois Tigeot &glob->kobj, &ttm_mem_glob_kobj_type, ttm_get_kobj(), "memory_accounting"); 3233a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 3243a2096e8SFrançois Tigeot kobject_put(&glob->kobj); 3253a2096e8SFrançois Tigeot return ret; 3263a2096e8SFrançois Tigeot } 3275718399fSFrançois Tigeot 328f6201ebfSMatthew Dillon /* 329f6201ebfSMatthew Dillon * Managed contiguous memory for TTM. Only use kernel-reserved 330f6201ebfSMatthew Dillon * dma memory for TTM, which can be controlled via /boot/loader.conf 331f6201ebfSMatthew Dillon * (e.g. vm.dma_reserved=256m). This is the only truly dependable 332f6201ebfSMatthew Dillon * DMA memory. 333f6201ebfSMatthew Dillon */ 334f6201ebfSMatthew Dillon mem = (uint64_t)vm_contig_avail_pages() * PAGE_SIZE; 3355718399fSFrançois Tigeot 3365718399fSFrançois Tigeot ret = ttm_mem_init_kernel_zone(glob, mem); 3375718399fSFrançois Tigeot if (unlikely(ret != 0)) 3385718399fSFrançois Tigeot goto out_no_zone; 33960b00aa3SFrançois Tigeot #ifdef CONFIG_HIGHMEM 34060b00aa3SFrançois Tigeot ret = ttm_mem_init_highmem_zone(glob, &si); 34160b00aa3SFrançois Tigeot if (unlikely(ret != 0)) 34260b00aa3SFrançois Tigeot goto out_no_zone; 34360b00aa3SFrançois Tigeot #else 3445718399fSFrançois Tigeot ret = ttm_mem_init_dma32_zone(glob, mem); 3455718399fSFrançois Tigeot if (unlikely(ret != 0)) 3465718399fSFrançois Tigeot goto out_no_zone; 34760b00aa3SFrançois Tigeot #endif 3485718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3495718399fSFrançois Tigeot zone = glob->zones[i]; 3500bece63dSImre Vadasz pr_info("Zone %7s: Available graphics memory: %llu kiB\n", 3515718399fSFrançois Tigeot zone->name, (unsigned long long)zone->max_mem >> 10); 3525718399fSFrançois Tigeot } 3535718399fSFrançois Tigeot ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 3545718399fSFrançois Tigeot ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 3555718399fSFrançois Tigeot return 0; 3565718399fSFrançois Tigeot out_no_zone: 3575718399fSFrançois Tigeot ttm_mem_global_release(glob); 3585718399fSFrançois Tigeot return ret; 3595718399fSFrançois Tigeot } 360c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_init); 3615718399fSFrançois Tigeot 3625718399fSFrançois Tigeot void ttm_mem_global_release(struct ttm_mem_global *glob) 3635718399fSFrançois Tigeot { 3645718399fSFrançois Tigeot unsigned int i; 3655718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3665718399fSFrançois Tigeot 3675718399fSFrançois Tigeot /* let the page allocator first stop the shrink work. */ 3685718399fSFrançois Tigeot ttm_page_alloc_fini(); 3695718399fSFrançois Tigeot ttm_dma_page_alloc_fini(); 3705718399fSFrançois Tigeot 37160b00aa3SFrançois Tigeot flush_workqueue(glob->swap_queue); 37260b00aa3SFrançois Tigeot destroy_workqueue(glob->swap_queue); 3735718399fSFrançois Tigeot glob->swap_queue = NULL; 3745718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3755718399fSFrançois Tigeot zone = glob->zones[i]; 3763a2096e8SFrançois Tigeot kobject_del(&zone->kobj); 3773a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 3785718399fSFrançois Tigeot } 3793a2096e8SFrançois Tigeot kobject_del(&glob->kobj); 3803a2096e8SFrançois Tigeot kobject_put(&glob->kobj); 3815718399fSFrançois Tigeot } 382c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_release); 3835718399fSFrançois Tigeot 3845718399fSFrançois Tigeot static void ttm_check_swapping(struct ttm_mem_global *glob) 3855718399fSFrançois Tigeot { 3865718399fSFrançois Tigeot bool needs_swapping = false; 3875718399fSFrançois Tigeot unsigned int i; 3885718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3895718399fSFrançois Tigeot 390ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 3915718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3925718399fSFrançois Tigeot zone = glob->zones[i]; 3935718399fSFrançois Tigeot if (zone->used_mem > zone->swap_limit) { 3945718399fSFrançois Tigeot needs_swapping = true; 3955718399fSFrançois Tigeot break; 3965718399fSFrançois Tigeot } 3975718399fSFrançois Tigeot } 398*932d855eSSergey Zigachev 399ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 4005718399fSFrançois Tigeot 4015718399fSFrançois Tigeot if (unlikely(needs_swapping)) 40260b00aa3SFrançois Tigeot (void)queue_work(glob->swap_queue, &glob->work); 4035718399fSFrançois Tigeot 4045718399fSFrançois Tigeot } 4055718399fSFrançois Tigeot 4065718399fSFrançois Tigeot static void ttm_mem_global_free_zone(struct ttm_mem_global *glob, 4075718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4085718399fSFrançois Tigeot uint64_t amount) 4095718399fSFrançois Tigeot { 4105718399fSFrançois Tigeot unsigned int i; 4115718399fSFrançois Tigeot struct ttm_mem_zone *zone; 4125718399fSFrançois Tigeot 413ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 4145718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4155718399fSFrançois Tigeot zone = glob->zones[i]; 4165718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4175718399fSFrançois Tigeot continue; 4185718399fSFrançois Tigeot zone->used_mem -= amount; 4195718399fSFrançois Tigeot } 420ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 4215718399fSFrançois Tigeot } 4225718399fSFrançois Tigeot 4235718399fSFrançois Tigeot void ttm_mem_global_free(struct ttm_mem_global *glob, 4245718399fSFrançois Tigeot uint64_t amount) 4255718399fSFrançois Tigeot { 42660b00aa3SFrançois Tigeot return ttm_mem_global_free_zone(glob, NULL, amount); 4275718399fSFrançois Tigeot } 428c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_free); 4295718399fSFrançois Tigeot 430*932d855eSSergey Zigachev /* 431*932d855eSSergey Zigachev * check if the available mem is under lower memory limit 432*932d855eSSergey Zigachev * 433*932d855eSSergey Zigachev * a. if no swap disk at all or free swap space is under swap_mem_limit 434*932d855eSSergey Zigachev * but available system mem is bigger than sys_mem_limit, allow TTM 435*932d855eSSergey Zigachev * allocation; 436*932d855eSSergey Zigachev * 437*932d855eSSergey Zigachev * b. if the available system mem is less than sys_mem_limit but free 438*932d855eSSergey Zigachev * swap disk is bigger than swap_mem_limit, allow TTM allocation. 439*932d855eSSergey Zigachev */ 440*932d855eSSergey Zigachev bool 441*932d855eSSergey Zigachev ttm_check_under_lowerlimit(struct ttm_mem_global *glob, 442*932d855eSSergey Zigachev uint64_t num_pages, 443*932d855eSSergey Zigachev struct ttm_operation_ctx *ctx) 444*932d855eSSergey Zigachev { 445*932d855eSSergey Zigachev STUB(); 446*932d855eSSergey Zigachev return false; 447*932d855eSSergey Zigachev #if 0 448*932d855eSSergey Zigachev int64_t available; 449*932d855eSSergey Zigachev 450*932d855eSSergey Zigachev if (ctx->flags & TTM_OPT_FLAG_FORCE_ALLOC) 451*932d855eSSergey Zigachev return false; 452*932d855eSSergey Zigachev 453*932d855eSSergey Zigachev available = get_nr_swap_pages() + si_mem_available(); 454*932d855eSSergey Zigachev available -= num_pages; 455*932d855eSSergey Zigachev if (available < glob->lower_mem_limit) 456*932d855eSSergey Zigachev return true; 457*932d855eSSergey Zigachev 458*932d855eSSergey Zigachev return false; 459*932d855eSSergey Zigachev #endif 460*932d855eSSergey Zigachev } 461*932d855eSSergey Zigachev EXPORT_SYMBOL(ttm_check_under_lowerlimit); 462*932d855eSSergey Zigachev 4635718399fSFrançois Tigeot static int ttm_mem_global_reserve(struct ttm_mem_global *glob, 4645718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4655718399fSFrançois Tigeot uint64_t amount, bool reserve) 4665718399fSFrançois Tigeot { 4675718399fSFrançois Tigeot uint64_t limit; 4685718399fSFrançois Tigeot int ret = -ENOMEM; 4695718399fSFrançois Tigeot unsigned int i; 4705718399fSFrançois Tigeot struct ttm_mem_zone *zone; 4715718399fSFrançois Tigeot 472ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 4735718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4745718399fSFrançois Tigeot zone = glob->zones[i]; 4755718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4765718399fSFrançois Tigeot continue; 4775718399fSFrançois Tigeot 4785718399fSFrançois Tigeot limit = (priv_check(curthread, PRIV_VM_MLOCK) == 0) ? 4795718399fSFrançois Tigeot zone->emer_mem : zone->max_mem; 4805718399fSFrançois Tigeot 4815718399fSFrançois Tigeot if (zone->used_mem > limit) 4825718399fSFrançois Tigeot goto out_unlock; 4835718399fSFrançois Tigeot } 4845718399fSFrançois Tigeot 4855718399fSFrançois Tigeot if (reserve) { 4865718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4875718399fSFrançois Tigeot zone = glob->zones[i]; 4885718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4895718399fSFrançois Tigeot continue; 4905718399fSFrançois Tigeot zone->used_mem += amount; 4915718399fSFrançois Tigeot } 4925718399fSFrançois Tigeot } 4935718399fSFrançois Tigeot 4945718399fSFrançois Tigeot ret = 0; 4955718399fSFrançois Tigeot out_unlock: 496ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 4975718399fSFrançois Tigeot ttm_check_swapping(glob); 4985718399fSFrançois Tigeot 4995718399fSFrançois Tigeot return ret; 5005718399fSFrançois Tigeot } 5015718399fSFrançois Tigeot 5025718399fSFrançois Tigeot 5035718399fSFrançois Tigeot static int ttm_mem_global_alloc_zone(struct ttm_mem_global *glob, 5045718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 5055718399fSFrançois Tigeot uint64_t memory, 506*932d855eSSergey Zigachev struct ttm_operation_ctx *ctx) 5075718399fSFrançois Tigeot { 5085718399fSFrançois Tigeot int count = TTM_MEMORY_ALLOC_RETRIES; 5095718399fSFrançois Tigeot 5105718399fSFrançois Tigeot while (unlikely(ttm_mem_global_reserve(glob, 5115718399fSFrançois Tigeot single_zone, 5125718399fSFrançois Tigeot memory, true) 5135718399fSFrançois Tigeot != 0)) { 514*932d855eSSergey Zigachev if (ctx->no_wait_gpu) 5155718399fSFrançois Tigeot return -ENOMEM; 5165718399fSFrançois Tigeot if (unlikely(count-- == 0)) 5175718399fSFrançois Tigeot return -ENOMEM; 518*932d855eSSergey Zigachev ttm_shrink(glob, false, memory + (memory >> 2) + 16, ctx); 5195718399fSFrançois Tigeot } 5205718399fSFrançois Tigeot 5215718399fSFrançois Tigeot return 0; 5225718399fSFrançois Tigeot } 5235718399fSFrançois Tigeot 5245718399fSFrançois Tigeot int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, 525*932d855eSSergey Zigachev struct ttm_operation_ctx *ctx) 5265718399fSFrançois Tigeot { 5275718399fSFrançois Tigeot /** 5285718399fSFrançois Tigeot * Normal allocations of kernel memory are registered in 5295718399fSFrançois Tigeot * all zones. 5305718399fSFrançois Tigeot */ 5315718399fSFrançois Tigeot 532*932d855eSSergey Zigachev return ttm_mem_global_alloc_zone(glob, NULL, memory, ctx); 5335718399fSFrançois Tigeot } 534c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_alloc); 5355718399fSFrançois Tigeot 5365718399fSFrançois Tigeot int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, 537*932d855eSSergey Zigachev struct page *page, uint64_t size, 538*932d855eSSergey Zigachev struct ttm_operation_ctx *ctx) 5395718399fSFrançois Tigeot { 5405718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 5415718399fSFrançois Tigeot 5425718399fSFrançois Tigeot /** 5435718399fSFrançois Tigeot * Page allocations may be registed in a single zone 5445718399fSFrançois Tigeot * only if highmem or !dma32. 5455718399fSFrançois Tigeot */ 5465718399fSFrançois Tigeot 5473f2dd94aSFrançois Tigeot #ifdef CONFIG_HIGHMEM 5483f2dd94aSFrançois Tigeot if (PageHighMem(page) && glob->zone_highmem != NULL) 5493f2dd94aSFrançois Tigeot zone = glob->zone_highmem; 5503f2dd94aSFrançois Tigeot #else 5515718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 5525718399fSFrançois Tigeot zone = glob->zone_kernel; 5533f2dd94aSFrançois Tigeot #endif 554*932d855eSSergey Zigachev return ttm_mem_global_alloc_zone(glob, zone, size, ctx); 5555718399fSFrançois Tigeot } 5565718399fSFrançois Tigeot 5573f2dd94aSFrançois Tigeot void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page, 5583f2dd94aSFrançois Tigeot uint64_t size) 5595718399fSFrançois Tigeot { 5605718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 5615718399fSFrançois Tigeot 5623f2dd94aSFrançois Tigeot #ifdef CONFIG_HIGHMEM 5633f2dd94aSFrançois Tigeot if (PageHighMem(page) && glob->zone_highmem != NULL) 5643f2dd94aSFrançois Tigeot zone = glob->zone_highmem; 5653f2dd94aSFrançois Tigeot #else 5665718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 5675718399fSFrançois Tigeot zone = glob->zone_kernel; 5683f2dd94aSFrançois Tigeot #endif 5693f2dd94aSFrançois Tigeot ttm_mem_global_free_zone(glob, zone, size); 5705718399fSFrançois Tigeot } 5715718399fSFrançois Tigeot 5725718399fSFrançois Tigeot size_t ttm_round_pot(size_t size) 5735718399fSFrançois Tigeot { 5745718399fSFrançois Tigeot if ((size & (size - 1)) == 0) 5755718399fSFrançois Tigeot return size; 5765718399fSFrançois Tigeot else if (size > PAGE_SIZE) 5775718399fSFrançois Tigeot return PAGE_ALIGN(size); 5785718399fSFrançois Tigeot else { 5795718399fSFrançois Tigeot size_t tmp_size = 4; 5805718399fSFrançois Tigeot 5815718399fSFrançois Tigeot while (tmp_size < size) 5825718399fSFrançois Tigeot tmp_size <<= 1; 5835718399fSFrançois Tigeot 5845718399fSFrançois Tigeot return tmp_size; 5855718399fSFrançois Tigeot } 5865718399fSFrançois Tigeot return 0; 5875718399fSFrançois Tigeot } 588c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_round_pot); 5891dedbd3bSFrançois Tigeot 5901dedbd3bSFrançois Tigeot uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob) 5911dedbd3bSFrançois Tigeot { 5921dedbd3bSFrançois Tigeot return glob->zone_kernel->max_mem; 5931dedbd3bSFrançois Tigeot } 5941dedbd3bSFrançois Tigeot EXPORT_SYMBOL(ttm_get_kernel_zone_memory_size); 595