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 * $FreeBSD: head/sys/dev/drm2/ttm/ttm_memory.c 248663 2013-03-23 20:46:47Z dumbbell $ 275718399fSFrançois Tigeot **************************************************************************/ 285718399fSFrançois Tigeot 290bece63dSImre Vadasz #define pr_fmt(fmt) "[TTM] " fmt 300bece63dSImre Vadasz 3118e26a6dSFrançois Tigeot #include <drm/drmP.h> 32216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_memory.h> 33216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_module.h> 34216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_page_alloc.h> 35c19c6249SFrançois Tigeot #include <linux/export.h> 365718399fSFrançois Tigeot 375718399fSFrançois Tigeot #define TTM_MEMORY_ALLOC_RETRIES 4 385718399fSFrançois Tigeot 395718399fSFrançois Tigeot struct ttm_mem_zone { 403a2096e8SFrançois Tigeot struct kobject kobj; 415718399fSFrançois Tigeot struct ttm_mem_global *glob; 425718399fSFrançois Tigeot const char *name; 435718399fSFrançois Tigeot uint64_t zone_mem; 445718399fSFrançois Tigeot uint64_t emer_mem; 455718399fSFrançois Tigeot uint64_t max_mem; 465718399fSFrançois Tigeot uint64_t swap_limit; 475718399fSFrançois Tigeot uint64_t used_mem; 485718399fSFrançois Tigeot }; 495718399fSFrançois Tigeot 503a2096e8SFrançois Tigeot static struct attribute ttm_mem_sys = { 513a2096e8SFrançois Tigeot .name = "zone_memory", 523a2096e8SFrançois Tigeot .mode = S_IRUGO 533a2096e8SFrançois Tigeot }; 543a2096e8SFrançois Tigeot static struct attribute ttm_mem_emer = { 553a2096e8SFrançois Tigeot .name = "emergency_memory", 563a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 573a2096e8SFrançois Tigeot }; 583a2096e8SFrançois Tigeot static struct attribute ttm_mem_max = { 593a2096e8SFrançois Tigeot .name = "available_memory", 603a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 613a2096e8SFrançois Tigeot }; 623a2096e8SFrançois Tigeot static struct attribute ttm_mem_swap = { 633a2096e8SFrançois Tigeot .name = "swap_limit", 643a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 653a2096e8SFrançois Tigeot }; 663a2096e8SFrançois Tigeot static struct attribute ttm_mem_used = { 673a2096e8SFrançois Tigeot .name = "used_memory", 683a2096e8SFrançois Tigeot .mode = S_IRUGO 693a2096e8SFrançois Tigeot }; 703a2096e8SFrançois Tigeot 713a2096e8SFrançois Tigeot static void ttm_mem_zone_kobj_release(struct kobject *kobj) 725718399fSFrançois Tigeot { 733a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 743a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_zone, kobj); 755718399fSFrançois Tigeot 760bece63dSImre Vadasz pr_info("Zone %7s: Used memory at exit: %llu kiB\n", 775718399fSFrançois Tigeot zone->name, (unsigned long long)zone->used_mem >> 10); 78175896dfSzrj kfree(zone); 795718399fSFrançois Tigeot } 805718399fSFrançois Tigeot 813a2096e8SFrançois Tigeot static ssize_t ttm_mem_zone_show(struct kobject *kobj, 825718399fSFrançois Tigeot struct attribute *attr, 835718399fSFrançois Tigeot char *buffer) 845718399fSFrançois Tigeot { 853a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 863a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_zone, kobj); 875718399fSFrançois Tigeot uint64_t val = 0; 885718399fSFrançois Tigeot 89*ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_EXCLUSIVE); 905718399fSFrançois Tigeot if (attr == &ttm_mem_sys) 915718399fSFrançois Tigeot val = zone->zone_mem; 925718399fSFrançois Tigeot else if (attr == &ttm_mem_emer) 935718399fSFrançois Tigeot val = zone->emer_mem; 945718399fSFrançois Tigeot else if (attr == &ttm_mem_max) 955718399fSFrançois Tigeot val = zone->max_mem; 965718399fSFrançois Tigeot else if (attr == &ttm_mem_swap) 975718399fSFrançois Tigeot val = zone->swap_limit; 985718399fSFrançois Tigeot else if (attr == &ttm_mem_used) 995718399fSFrançois Tigeot val = zone->used_mem; 100*ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_RELEASE); 1015718399fSFrançois Tigeot 1023a2096e8SFrançois Tigeot return ksnprintf(buffer, PAGE_SIZE, "%llu\n", 1035718399fSFrançois Tigeot (unsigned long long) val >> 10); 1045718399fSFrançois Tigeot } 1055718399fSFrançois Tigeot 1065718399fSFrançois Tigeot static void ttm_check_swapping(struct ttm_mem_global *glob); 1075718399fSFrançois Tigeot 1083a2096e8SFrançois Tigeot static ssize_t ttm_mem_zone_store(struct kobject *kobj, 1095718399fSFrançois Tigeot struct attribute *attr, 1105718399fSFrançois Tigeot const char *buffer, 1115718399fSFrançois Tigeot size_t size) 1125718399fSFrançois Tigeot { 1133a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 1143a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_zone, kobj); 1155718399fSFrançois Tigeot int chars; 1165718399fSFrançois Tigeot unsigned long val; 1175718399fSFrançois Tigeot uint64_t val64; 1185718399fSFrançois Tigeot 1193a2096e8SFrançois Tigeot chars = ksscanf(buffer, "%lu", &val); 1205718399fSFrançois Tigeot if (chars == 0) 1215718399fSFrançois Tigeot return size; 1225718399fSFrançois Tigeot 1235718399fSFrançois Tigeot val64 = val; 1245718399fSFrançois Tigeot val64 <<= 10; 1255718399fSFrançois Tigeot 126*ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_EXCLUSIVE); 1275718399fSFrançois Tigeot if (val64 > zone->zone_mem) 1285718399fSFrançois Tigeot val64 = zone->zone_mem; 1295718399fSFrançois Tigeot if (attr == &ttm_mem_emer) { 1305718399fSFrançois Tigeot zone->emer_mem = val64; 1315718399fSFrançois Tigeot if (zone->max_mem > val64) 1325718399fSFrançois Tigeot zone->max_mem = val64; 1335718399fSFrançois Tigeot } else if (attr == &ttm_mem_max) { 1345718399fSFrançois Tigeot zone->max_mem = val64; 1355718399fSFrançois Tigeot if (zone->emer_mem < val64) 1365718399fSFrançois Tigeot zone->emer_mem = val64; 1375718399fSFrançois Tigeot } else if (attr == &ttm_mem_swap) 1385718399fSFrançois Tigeot zone->swap_limit = val64; 139*ec5b6af4SFrançois Tigeot lockmgr(&zone->glob->lock, LK_RELEASE); 1405718399fSFrançois Tigeot 1415718399fSFrançois Tigeot ttm_check_swapping(zone->glob); 1425718399fSFrançois Tigeot 1435718399fSFrançois Tigeot return size; 1445718399fSFrançois Tigeot } 1455718399fSFrançois Tigeot 1463a2096e8SFrançois Tigeot static struct attribute *ttm_mem_zone_attrs[] = { 1473a2096e8SFrançois Tigeot &ttm_mem_sys, 1483a2096e8SFrançois Tigeot &ttm_mem_emer, 1493a2096e8SFrançois Tigeot &ttm_mem_max, 1503a2096e8SFrançois Tigeot &ttm_mem_swap, 1513a2096e8SFrançois Tigeot &ttm_mem_used, 1523a2096e8SFrançois Tigeot NULL 1533a2096e8SFrançois Tigeot }; 1543a2096e8SFrançois Tigeot 1553a2096e8SFrançois Tigeot static const struct sysfs_ops ttm_mem_zone_ops = { 1563a2096e8SFrançois Tigeot .show = &ttm_mem_zone_show, 1573a2096e8SFrançois Tigeot .store = &ttm_mem_zone_store 1583a2096e8SFrançois Tigeot }; 1593a2096e8SFrançois Tigeot 1603a2096e8SFrançois Tigeot static struct kobj_type ttm_mem_zone_kobj_type = { 1613a2096e8SFrançois Tigeot .release = &ttm_mem_zone_kobj_release, 1623a2096e8SFrançois Tigeot .sysfs_ops = &ttm_mem_zone_ops, 1633a2096e8SFrançois Tigeot .default_attrs = ttm_mem_zone_attrs, 1643a2096e8SFrançois Tigeot }; 1653a2096e8SFrançois Tigeot 1663a2096e8SFrançois Tigeot static void ttm_mem_global_kobj_release(struct kobject *kobj) 1675718399fSFrançois Tigeot { 1683a2096e8SFrançois Tigeot struct ttm_mem_global *glob = 1693a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_global, kobj); 1703a2096e8SFrançois Tigeot 1713a2096e8SFrançois Tigeot kfree(glob); 1725718399fSFrançois Tigeot } 1735718399fSFrançois Tigeot 1743a2096e8SFrançois Tigeot static struct kobj_type ttm_mem_glob_kobj_type = { 1753a2096e8SFrançois Tigeot .release = &ttm_mem_global_kobj_release, 1763a2096e8SFrançois Tigeot }; 1773a2096e8SFrançois Tigeot 1785718399fSFrançois Tigeot static bool ttm_zones_above_swap_target(struct ttm_mem_global *glob, 1795718399fSFrançois Tigeot bool from_wq, uint64_t extra) 1805718399fSFrançois Tigeot { 1815718399fSFrançois Tigeot unsigned int i; 1825718399fSFrançois Tigeot struct ttm_mem_zone *zone; 1835718399fSFrançois Tigeot uint64_t target; 1845718399fSFrançois Tigeot 1855718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 1865718399fSFrançois Tigeot zone = glob->zones[i]; 1875718399fSFrançois Tigeot 1885718399fSFrançois Tigeot if (from_wq) 1895718399fSFrançois Tigeot target = zone->swap_limit; 1905718399fSFrançois Tigeot else if (priv_check(curthread, PRIV_VM_MLOCK) == 0) 1915718399fSFrançois Tigeot target = zone->emer_mem; 1925718399fSFrançois Tigeot else 1935718399fSFrançois Tigeot target = zone->max_mem; 1945718399fSFrançois Tigeot 1955718399fSFrançois Tigeot target = (extra > target) ? 0ULL : target; 1965718399fSFrançois Tigeot 1975718399fSFrançois Tigeot if (zone->used_mem > target) 1985718399fSFrançois Tigeot return true; 1995718399fSFrançois Tigeot } 2005718399fSFrançois Tigeot return false; 2015718399fSFrançois Tigeot } 2025718399fSFrançois Tigeot 2035718399fSFrançois Tigeot /** 2045718399fSFrançois Tigeot * At this point we only support a single shrink callback. 2055718399fSFrançois Tigeot * Extend this if needed, perhaps using a linked list of callbacks. 2065718399fSFrançois Tigeot * Note that this function is reentrant: 2075718399fSFrançois Tigeot * many threads may try to swap out at any given time. 2085718399fSFrançois Tigeot */ 2095718399fSFrançois Tigeot 2105718399fSFrançois Tigeot static void ttm_shrink(struct ttm_mem_global *glob, bool from_wq, 2115718399fSFrançois Tigeot uint64_t extra) 2125718399fSFrançois Tigeot { 2135718399fSFrançois Tigeot int ret; 2145718399fSFrançois Tigeot struct ttm_mem_shrink *shrink; 2155718399fSFrançois Tigeot 216*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 2175718399fSFrançois Tigeot if (glob->shrink == NULL) 2185718399fSFrançois Tigeot goto out; 2195718399fSFrançois Tigeot 2205718399fSFrançois Tigeot while (ttm_zones_above_swap_target(glob, from_wq, extra)) { 2215718399fSFrançois Tigeot shrink = glob->shrink; 222*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 223f6201ebfSMatthew Dillon ret = shrink->do_shrink(shrink); 224*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 2255718399fSFrançois Tigeot if (unlikely(ret != 0)) 2265718399fSFrançois Tigeot goto out; 2275718399fSFrançois Tigeot } 2285718399fSFrançois Tigeot out: 229*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 2305718399fSFrançois Tigeot } 2315718399fSFrançois Tigeot 2325718399fSFrançois Tigeot 2335718399fSFrançois Tigeot 2345718399fSFrançois Tigeot static void ttm_shrink_work(void *arg, int pending __unused) 2355718399fSFrançois Tigeot { 2365718399fSFrançois Tigeot struct ttm_mem_global *glob = arg; 2375718399fSFrançois Tigeot 2385718399fSFrançois Tigeot ttm_shrink(glob, true, 0ULL); 2395718399fSFrançois Tigeot } 2405718399fSFrançois Tigeot 2415718399fSFrançois Tigeot static int ttm_mem_init_kernel_zone(struct ttm_mem_global *glob, 2425718399fSFrançois Tigeot uint64_t mem) 2435718399fSFrançois Tigeot { 2443a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL); 2453a2096e8SFrançois Tigeot int ret; 2465718399fSFrançois Tigeot 2475718399fSFrançois Tigeot zone->name = "kernel"; 2485718399fSFrançois Tigeot zone->zone_mem = mem; 2495718399fSFrançois Tigeot zone->max_mem = mem >> 1; 2505718399fSFrançois Tigeot zone->emer_mem = (mem >> 1) + (mem >> 2); 2515718399fSFrançois Tigeot zone->swap_limit = zone->max_mem - (mem >> 3); 2525718399fSFrançois Tigeot zone->used_mem = 0; 2535718399fSFrançois Tigeot zone->glob = glob; 2545718399fSFrançois Tigeot glob->zone_kernel = zone; 2553a2096e8SFrançois Tigeot ret = kobject_init_and_add( 2563a2096e8SFrançois Tigeot &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, zone->name); 2573a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 2583a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 2593a2096e8SFrançois Tigeot return ret; 2603a2096e8SFrançois Tigeot } 2615718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 2625718399fSFrançois Tigeot return 0; 2635718399fSFrançois Tigeot } 2645718399fSFrançois Tigeot 2653a2096e8SFrançois Tigeot #ifdef CONFIG_HIGHMEM 2663a2096e8SFrançois Tigeot #else 2675718399fSFrançois Tigeot static int ttm_mem_init_dma32_zone(struct ttm_mem_global *glob, 2685718399fSFrançois Tigeot uint64_t mem) 2695718399fSFrançois Tigeot { 2703a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL); 2713a2096e8SFrançois Tigeot int ret; 2725718399fSFrançois Tigeot 2735718399fSFrançois Tigeot /** 2745718399fSFrançois Tigeot * No special dma32 zone needed. 2755718399fSFrançois Tigeot */ 2765718399fSFrançois Tigeot 277f6201ebfSMatthew Dillon if ((physmem * PAGE_SIZE) <= ((uint64_t) 1ULL << 32)) { 278175896dfSzrj kfree(zone); 2795718399fSFrançois Tigeot return 0; 2805718399fSFrançois Tigeot } 2815718399fSFrançois Tigeot 2825718399fSFrançois Tigeot /* 2835718399fSFrançois Tigeot * Limit max dma32 memory to 4GB for now 2845718399fSFrançois Tigeot * until we can figure out how big this 2855718399fSFrançois Tigeot * zone really is. 2865718399fSFrançois Tigeot */ 287f6201ebfSMatthew Dillon if (mem > ((uint64_t) 1ULL << 32)) 2885718399fSFrançois Tigeot mem = ((uint64_t) 1ULL << 32); 289f6201ebfSMatthew Dillon 2905718399fSFrançois Tigeot zone->name = "dma32"; 2915718399fSFrançois Tigeot zone->zone_mem = mem; 2925718399fSFrançois Tigeot zone->max_mem = mem >> 1; 2935718399fSFrançois Tigeot zone->emer_mem = (mem >> 1) + (mem >> 2); 2945718399fSFrançois Tigeot zone->swap_limit = zone->max_mem - (mem >> 3); 2955718399fSFrançois Tigeot zone->used_mem = 0; 2965718399fSFrançois Tigeot zone->glob = glob; 2975718399fSFrançois Tigeot glob->zone_dma32 = zone; 2983a2096e8SFrançois Tigeot ret = kobject_init_and_add( 2991cfef1a5SFrançois Tigeot &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, "%s", 3001cfef1a5SFrançois Tigeot zone->name); 3013a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 3023a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 3033a2096e8SFrançois Tigeot return ret; 3043a2096e8SFrançois Tigeot } 3055718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 3065718399fSFrançois Tigeot return 0; 3075718399fSFrançois Tigeot } 3083a2096e8SFrançois Tigeot #endif 3095718399fSFrançois Tigeot 3105718399fSFrançois Tigeot int ttm_mem_global_init(struct ttm_mem_global *glob) 3115718399fSFrançois Tigeot { 3125718399fSFrançois Tigeot u_int64_t mem; 3135718399fSFrançois Tigeot int ret; 3145718399fSFrançois Tigeot int i; 3155718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3165718399fSFrançois Tigeot 317*ec5b6af4SFrançois Tigeot lockinit(&glob->lock, "ttmemglob", 0, LK_EXCLUSIVE); 3185718399fSFrançois Tigeot glob->swap_queue = taskqueue_create("ttm_swap", M_WAITOK, 3195718399fSFrançois Tigeot taskqueue_thread_enqueue, &glob->swap_queue); 320acb1fe1aSMatthew Dillon taskqueue_start_threads(&glob->swap_queue, 1, TDPRI_KERN_DAEMON, 321acb1fe1aSMatthew Dillon -1, "ttm swap"); 3225718399fSFrançois Tigeot TASK_INIT(&glob->work, 0, ttm_shrink_work, glob); 3233a2096e8SFrançois Tigeot ret = kobject_init_and_add( 3243a2096e8SFrançois Tigeot &glob->kobj, &ttm_mem_glob_kobj_type, ttm_get_kobj(), "memory_accounting"); 3253a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 3263a2096e8SFrançois Tigeot kobject_put(&glob->kobj); 3273a2096e8SFrançois Tigeot return ret; 3283a2096e8SFrançois Tigeot } 3295718399fSFrançois Tigeot 330f6201ebfSMatthew Dillon /* 331f6201ebfSMatthew Dillon * Managed contiguous memory for TTM. Only use kernel-reserved 332f6201ebfSMatthew Dillon * dma memory for TTM, which can be controlled via /boot/loader.conf 333f6201ebfSMatthew Dillon * (e.g. vm.dma_reserved=256m). This is the only truly dependable 334f6201ebfSMatthew Dillon * DMA memory. 335f6201ebfSMatthew Dillon */ 336f6201ebfSMatthew Dillon mem = (uint64_t)vm_contig_avail_pages() * PAGE_SIZE; 3375718399fSFrançois Tigeot 3385718399fSFrançois Tigeot ret = ttm_mem_init_kernel_zone(glob, mem); 3395718399fSFrançois Tigeot if (unlikely(ret != 0)) 3405718399fSFrançois Tigeot goto out_no_zone; 3415718399fSFrançois Tigeot ret = ttm_mem_init_dma32_zone(glob, mem); 3425718399fSFrançois Tigeot if (unlikely(ret != 0)) 3435718399fSFrançois Tigeot goto out_no_zone; 3440bece63dSImre Vadasz pr_info("(struct ttm_mem_global *)%p\n", glob); 3455718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3465718399fSFrançois Tigeot zone = glob->zones[i]; 3470bece63dSImre Vadasz pr_info("Zone %7s: Available graphics memory: %llu kiB\n", 3485718399fSFrançois Tigeot zone->name, (unsigned long long)zone->max_mem >> 10); 3495718399fSFrançois Tigeot } 3505718399fSFrançois Tigeot ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 3515718399fSFrançois Tigeot ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 3525718399fSFrançois Tigeot return 0; 3535718399fSFrançois Tigeot out_no_zone: 3545718399fSFrançois Tigeot ttm_mem_global_release(glob); 3555718399fSFrançois Tigeot return ret; 3565718399fSFrançois Tigeot } 357c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_init); 3585718399fSFrançois Tigeot 3595718399fSFrançois Tigeot void ttm_mem_global_release(struct ttm_mem_global *glob) 3605718399fSFrançois Tigeot { 3615718399fSFrançois Tigeot unsigned int i; 3625718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3635718399fSFrançois Tigeot 3645718399fSFrançois Tigeot /* let the page allocator first stop the shrink work. */ 3655718399fSFrançois Tigeot ttm_page_alloc_fini(); 3665718399fSFrançois Tigeot ttm_dma_page_alloc_fini(); 3675718399fSFrançois Tigeot 3685718399fSFrançois Tigeot taskqueue_drain(glob->swap_queue, &glob->work); 3695718399fSFrançois Tigeot taskqueue_free(glob->swap_queue); 3705718399fSFrançois Tigeot glob->swap_queue = NULL; 3715718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3725718399fSFrançois Tigeot zone = glob->zones[i]; 3733a2096e8SFrançois Tigeot kobject_del(&zone->kobj); 3743a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 3755718399fSFrançois Tigeot } 3763a2096e8SFrançois Tigeot kobject_del(&glob->kobj); 3773a2096e8SFrançois Tigeot kobject_put(&glob->kobj); 3783a2096e8SFrançois Tigeot 3795718399fSFrançois Tigeot } 380c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_release); 3815718399fSFrançois Tigeot 3825718399fSFrançois Tigeot static void ttm_check_swapping(struct ttm_mem_global *glob) 3835718399fSFrançois Tigeot { 3845718399fSFrançois Tigeot bool needs_swapping = false; 3855718399fSFrançois Tigeot unsigned int i; 3865718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3875718399fSFrançois Tigeot 388*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 3895718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3905718399fSFrançois Tigeot zone = glob->zones[i]; 3915718399fSFrançois Tigeot if (zone->used_mem > zone->swap_limit) { 3925718399fSFrançois Tigeot needs_swapping = true; 3935718399fSFrançois Tigeot break; 3945718399fSFrançois Tigeot } 3955718399fSFrançois Tigeot } 396*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 3975718399fSFrançois Tigeot 3985718399fSFrançois Tigeot if (unlikely(needs_swapping)) 3995718399fSFrançois Tigeot taskqueue_enqueue(glob->swap_queue, &glob->work); 4005718399fSFrançois Tigeot 4015718399fSFrançois Tigeot } 4025718399fSFrançois Tigeot 4035718399fSFrançois Tigeot static void ttm_mem_global_free_zone(struct ttm_mem_global *glob, 4045718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4055718399fSFrançois Tigeot uint64_t amount) 4065718399fSFrançois Tigeot { 4075718399fSFrançois Tigeot unsigned int i; 4085718399fSFrançois Tigeot struct ttm_mem_zone *zone; 4095718399fSFrançois Tigeot 410*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 4115718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4125718399fSFrançois Tigeot zone = glob->zones[i]; 4135718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4145718399fSFrançois Tigeot continue; 4155718399fSFrançois Tigeot zone->used_mem -= amount; 4165718399fSFrançois Tigeot } 417*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 4185718399fSFrançois Tigeot } 4195718399fSFrançois Tigeot 4205718399fSFrançois Tigeot void ttm_mem_global_free(struct ttm_mem_global *glob, 4215718399fSFrançois Tigeot uint64_t amount) 4225718399fSFrançois Tigeot { 423111e70c7SSascha Wildner ttm_mem_global_free_zone(glob, NULL, amount); 4245718399fSFrançois Tigeot } 425c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_free); 4265718399fSFrançois Tigeot 4275718399fSFrançois Tigeot static int ttm_mem_global_reserve(struct ttm_mem_global *glob, 4285718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4295718399fSFrançois Tigeot uint64_t amount, bool reserve) 4305718399fSFrançois Tigeot { 4315718399fSFrançois Tigeot uint64_t limit; 4325718399fSFrançois Tigeot int ret = -ENOMEM; 4335718399fSFrançois Tigeot unsigned int i; 4345718399fSFrançois Tigeot struct ttm_mem_zone *zone; 4355718399fSFrançois Tigeot 436*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_EXCLUSIVE); 4375718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4385718399fSFrançois Tigeot zone = glob->zones[i]; 4395718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4405718399fSFrançois Tigeot continue; 4415718399fSFrançois Tigeot 4425718399fSFrançois Tigeot limit = (priv_check(curthread, PRIV_VM_MLOCK) == 0) ? 4435718399fSFrançois Tigeot zone->emer_mem : zone->max_mem; 4445718399fSFrançois Tigeot 4455718399fSFrançois Tigeot if (zone->used_mem > limit) 4465718399fSFrançois Tigeot goto out_unlock; 4475718399fSFrançois Tigeot } 4485718399fSFrançois Tigeot 4495718399fSFrançois Tigeot if (reserve) { 4505718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4515718399fSFrançois Tigeot zone = glob->zones[i]; 4525718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4535718399fSFrançois Tigeot continue; 4545718399fSFrançois Tigeot zone->used_mem += amount; 4555718399fSFrançois Tigeot } 4565718399fSFrançois Tigeot } 4575718399fSFrançois Tigeot 4585718399fSFrançois Tigeot ret = 0; 4595718399fSFrançois Tigeot out_unlock: 460*ec5b6af4SFrançois Tigeot lockmgr(&glob->lock, LK_RELEASE); 4615718399fSFrançois Tigeot ttm_check_swapping(glob); 4625718399fSFrançois Tigeot 4635718399fSFrançois Tigeot return ret; 4645718399fSFrançois Tigeot } 4655718399fSFrançois Tigeot 4665718399fSFrançois Tigeot 4675718399fSFrançois Tigeot static int ttm_mem_global_alloc_zone(struct ttm_mem_global *glob, 4685718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4695718399fSFrançois Tigeot uint64_t memory, 4705718399fSFrançois Tigeot bool no_wait, bool interruptible) 4715718399fSFrançois Tigeot { 4725718399fSFrançois Tigeot int count = TTM_MEMORY_ALLOC_RETRIES; 4735718399fSFrançois Tigeot 4745718399fSFrançois Tigeot while (unlikely(ttm_mem_global_reserve(glob, 4755718399fSFrançois Tigeot single_zone, 4765718399fSFrançois Tigeot memory, true) 4775718399fSFrançois Tigeot != 0)) { 4785718399fSFrançois Tigeot if (no_wait) 4795718399fSFrançois Tigeot return -ENOMEM; 4805718399fSFrançois Tigeot if (unlikely(count-- == 0)) 4815718399fSFrançois Tigeot return -ENOMEM; 4825718399fSFrançois Tigeot ttm_shrink(glob, false, memory + (memory >> 2) + 16); 4835718399fSFrançois Tigeot } 4845718399fSFrançois Tigeot 4855718399fSFrançois Tigeot return 0; 4865718399fSFrançois Tigeot } 4875718399fSFrançois Tigeot 4885718399fSFrançois Tigeot int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, 4895718399fSFrançois Tigeot bool no_wait, bool interruptible) 4905718399fSFrançois Tigeot { 4915718399fSFrançois Tigeot /** 4925718399fSFrançois Tigeot * Normal allocations of kernel memory are registered in 4935718399fSFrançois Tigeot * all zones. 4945718399fSFrançois Tigeot */ 4955718399fSFrançois Tigeot 4965718399fSFrançois Tigeot return ttm_mem_global_alloc_zone(glob, NULL, memory, no_wait, 4975718399fSFrançois Tigeot interruptible); 4985718399fSFrançois Tigeot } 499c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_alloc); 5005718399fSFrançois Tigeot 5015718399fSFrançois Tigeot int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, 502f0bba3d1SFrançois Tigeot struct page *page, 5035718399fSFrançois Tigeot bool no_wait, bool interruptible) 5045718399fSFrançois Tigeot { 5055718399fSFrançois Tigeot 5065718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 5075718399fSFrançois Tigeot 5085718399fSFrançois Tigeot /** 5095718399fSFrançois Tigeot * Page allocations may be registed in a single zone 5105718399fSFrançois Tigeot * only if highmem or !dma32. 5115718399fSFrançois Tigeot */ 5125718399fSFrançois Tigeot 5135718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 5145718399fSFrançois Tigeot zone = glob->zone_kernel; 5155718399fSFrançois Tigeot return ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, no_wait, 5165718399fSFrançois Tigeot interruptible); 5175718399fSFrançois Tigeot } 5185718399fSFrançois Tigeot 519f0bba3d1SFrançois Tigeot void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page) 5205718399fSFrançois Tigeot { 5215718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 5225718399fSFrançois Tigeot 5235718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 5245718399fSFrançois Tigeot zone = glob->zone_kernel; 5255718399fSFrançois Tigeot ttm_mem_global_free_zone(glob, zone, PAGE_SIZE); 5265718399fSFrançois Tigeot } 5275718399fSFrançois Tigeot 5285718399fSFrançois Tigeot 5295718399fSFrançois Tigeot size_t ttm_round_pot(size_t size) 5305718399fSFrançois Tigeot { 5315718399fSFrançois Tigeot if ((size & (size - 1)) == 0) 5325718399fSFrançois Tigeot return size; 5335718399fSFrançois Tigeot else if (size > PAGE_SIZE) 5345718399fSFrançois Tigeot return PAGE_ALIGN(size); 5355718399fSFrançois Tigeot else { 5365718399fSFrançois Tigeot size_t tmp_size = 4; 5375718399fSFrançois Tigeot 5385718399fSFrançois Tigeot while (tmp_size < size) 5395718399fSFrançois Tigeot tmp_size <<= 1; 5405718399fSFrançois Tigeot 5415718399fSFrançois Tigeot return tmp_size; 5425718399fSFrançois Tigeot } 5435718399fSFrançois Tigeot return 0; 5445718399fSFrançois Tigeot } 545c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_round_pot); 5461dedbd3bSFrançois Tigeot 5471dedbd3bSFrançois Tigeot uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob) 5481dedbd3bSFrançois Tigeot { 5491dedbd3bSFrançois Tigeot return glob->zone_kernel->max_mem; 5501dedbd3bSFrançois Tigeot } 5511dedbd3bSFrançois Tigeot EXPORT_SYMBOL(ttm_get_kernel_zone_memory_size); 552