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 { 40*3a2096e8SFranç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 50*3a2096e8SFrançois Tigeot static struct attribute ttm_mem_sys = { 51*3a2096e8SFrançois Tigeot .name = "zone_memory", 52*3a2096e8SFrançois Tigeot .mode = S_IRUGO 53*3a2096e8SFrançois Tigeot }; 54*3a2096e8SFrançois Tigeot static struct attribute ttm_mem_emer = { 55*3a2096e8SFrançois Tigeot .name = "emergency_memory", 56*3a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 57*3a2096e8SFrançois Tigeot }; 58*3a2096e8SFrançois Tigeot static struct attribute ttm_mem_max = { 59*3a2096e8SFrançois Tigeot .name = "available_memory", 60*3a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 61*3a2096e8SFrançois Tigeot }; 62*3a2096e8SFrançois Tigeot static struct attribute ttm_mem_swap = { 63*3a2096e8SFrançois Tigeot .name = "swap_limit", 64*3a2096e8SFrançois Tigeot .mode = S_IRUGO | S_IWUSR 65*3a2096e8SFrançois Tigeot }; 66*3a2096e8SFrançois Tigeot static struct attribute ttm_mem_used = { 67*3a2096e8SFrançois Tigeot .name = "used_memory", 68*3a2096e8SFrançois Tigeot .mode = S_IRUGO 69*3a2096e8SFrançois Tigeot }; 70*3a2096e8SFrançois Tigeot 71*3a2096e8SFrançois Tigeot static void ttm_mem_zone_kobj_release(struct kobject *kobj) 725718399fSFrançois Tigeot { 73*3a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 74*3a2096e8SFranç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 81*3a2096e8SFranç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 { 85*3a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 86*3a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_zone, kobj); 875718399fSFrançois Tigeot uint64_t val = 0; 885718399fSFrançois Tigeot 89*3a2096e8SFrançois Tigeot spin_lock(&zone->glob->spin); 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*3a2096e8SFrançois Tigeot spin_unlock(&zone->glob->spin); 1015718399fSFrançois Tigeot 102*3a2096e8SFranç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 108*3a2096e8SFranç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 { 113*3a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = 114*3a2096e8SFranç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 119*3a2096e8SFranç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*3a2096e8SFrançois Tigeot spin_lock(&zone->glob->spin); 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*3a2096e8SFrançois Tigeot spin_unlock(&zone->glob->spin); 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 146*3a2096e8SFrançois Tigeot static struct attribute *ttm_mem_zone_attrs[] = { 147*3a2096e8SFrançois Tigeot &ttm_mem_sys, 148*3a2096e8SFrançois Tigeot &ttm_mem_emer, 149*3a2096e8SFrançois Tigeot &ttm_mem_max, 150*3a2096e8SFrançois Tigeot &ttm_mem_swap, 151*3a2096e8SFrançois Tigeot &ttm_mem_used, 152*3a2096e8SFrançois Tigeot NULL 153*3a2096e8SFrançois Tigeot }; 154*3a2096e8SFrançois Tigeot 155*3a2096e8SFrançois Tigeot static const struct sysfs_ops ttm_mem_zone_ops = { 156*3a2096e8SFrançois Tigeot .show = &ttm_mem_zone_show, 157*3a2096e8SFrançois Tigeot .store = &ttm_mem_zone_store 158*3a2096e8SFrançois Tigeot }; 159*3a2096e8SFrançois Tigeot 160*3a2096e8SFrançois Tigeot static struct kobj_type ttm_mem_zone_kobj_type = { 161*3a2096e8SFrançois Tigeot .release = &ttm_mem_zone_kobj_release, 162*3a2096e8SFrançois Tigeot .sysfs_ops = &ttm_mem_zone_ops, 163*3a2096e8SFrançois Tigeot .default_attrs = ttm_mem_zone_attrs, 164*3a2096e8SFrançois Tigeot }; 165*3a2096e8SFrançois Tigeot 166*3a2096e8SFrançois Tigeot static void ttm_mem_global_kobj_release(struct kobject *kobj) 1675718399fSFrançois Tigeot { 168*3a2096e8SFrançois Tigeot struct ttm_mem_global *glob = 169*3a2096e8SFrançois Tigeot container_of(kobj, struct ttm_mem_global, kobj); 170*3a2096e8SFrançois Tigeot 171*3a2096e8SFrançois Tigeot kfree(glob); 1725718399fSFrançois Tigeot } 1735718399fSFrançois Tigeot 174*3a2096e8SFrançois Tigeot static struct kobj_type ttm_mem_glob_kobj_type = { 175*3a2096e8SFrançois Tigeot .release = &ttm_mem_global_kobj_release, 176*3a2096e8SFrançois Tigeot }; 177*3a2096e8SFranç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 2165718399fSFrançois Tigeot spin_lock(&glob->spin); 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; 2225718399fSFrançois Tigeot spin_unlock(&glob->spin); 223f6201ebfSMatthew Dillon ret = shrink->do_shrink(shrink); 224f6201ebfSMatthew Dillon spin_lock(&glob->spin); 2255718399fSFrançois Tigeot if (unlikely(ret != 0)) 2265718399fSFrançois Tigeot goto out; 2275718399fSFrançois Tigeot } 2285718399fSFrançois Tigeot out: 2295718399fSFrançois Tigeot spin_unlock(&glob->spin); 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 { 244*3a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL); 245*3a2096e8SFranç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; 255*3a2096e8SFrançois Tigeot ret = kobject_init_and_add( 256*3a2096e8SFrançois Tigeot &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, zone->name); 257*3a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 258*3a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 259*3a2096e8SFrançois Tigeot return ret; 260*3a2096e8SFrançois Tigeot } 2615718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 2625718399fSFrançois Tigeot return 0; 2635718399fSFrançois Tigeot } 2645718399fSFrançois Tigeot 265*3a2096e8SFrançois Tigeot #ifdef CONFIG_HIGHMEM 266*3a2096e8SFranç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 { 270*3a2096e8SFrançois Tigeot struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL); 271*3a2096e8SFranç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; 298*3a2096e8SFrançois Tigeot ret = kobject_init_and_add( 299*3a2096e8SFrançois Tigeot &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, zone->name); 300*3a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 301*3a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 302*3a2096e8SFrançois Tigeot return ret; 303*3a2096e8SFrançois Tigeot } 3045718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 3055718399fSFrançois Tigeot return 0; 3065718399fSFrançois Tigeot } 307*3a2096e8SFrançois Tigeot #endif 3085718399fSFrançois Tigeot 3095718399fSFrançois Tigeot int ttm_mem_global_init(struct ttm_mem_global *glob) 3105718399fSFrançois Tigeot { 3115718399fSFrançois Tigeot u_int64_t mem; 3125718399fSFrançois Tigeot int ret; 3135718399fSFrançois Tigeot int i; 3145718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3155718399fSFrançois Tigeot 316ba87a4abSSascha Wildner spin_init(&glob->spin, "ttmemglob"); 3175718399fSFrançois Tigeot glob->swap_queue = taskqueue_create("ttm_swap", M_WAITOK, 3185718399fSFrançois Tigeot taskqueue_thread_enqueue, &glob->swap_queue); 319acb1fe1aSMatthew Dillon taskqueue_start_threads(&glob->swap_queue, 1, TDPRI_KERN_DAEMON, 320acb1fe1aSMatthew Dillon -1, "ttm swap"); 3215718399fSFrançois Tigeot TASK_INIT(&glob->work, 0, ttm_shrink_work, glob); 322*3a2096e8SFrançois Tigeot ret = kobject_init_and_add( 323*3a2096e8SFrançois Tigeot &glob->kobj, &ttm_mem_glob_kobj_type, ttm_get_kobj(), "memory_accounting"); 324*3a2096e8SFrançois Tigeot if (unlikely(ret != 0)) { 325*3a2096e8SFrançois Tigeot kobject_put(&glob->kobj); 326*3a2096e8SFrançois Tigeot return ret; 327*3a2096e8SFrançois Tigeot } 3285718399fSFrançois Tigeot 329f6201ebfSMatthew Dillon /* 330f6201ebfSMatthew Dillon * Managed contiguous memory for TTM. Only use kernel-reserved 331f6201ebfSMatthew Dillon * dma memory for TTM, which can be controlled via /boot/loader.conf 332f6201ebfSMatthew Dillon * (e.g. vm.dma_reserved=256m). This is the only truly dependable 333f6201ebfSMatthew Dillon * DMA memory. 334f6201ebfSMatthew Dillon */ 335f6201ebfSMatthew Dillon mem = (uint64_t)vm_contig_avail_pages() * PAGE_SIZE; 3365718399fSFrançois Tigeot 3375718399fSFrançois Tigeot ret = ttm_mem_init_kernel_zone(glob, mem); 3385718399fSFrançois Tigeot if (unlikely(ret != 0)) 3395718399fSFrançois Tigeot goto out_no_zone; 3405718399fSFrançois Tigeot ret = ttm_mem_init_dma32_zone(glob, mem); 3415718399fSFrançois Tigeot if (unlikely(ret != 0)) 3425718399fSFrançois Tigeot goto out_no_zone; 3430bece63dSImre Vadasz pr_info("(struct ttm_mem_global *)%p\n", glob); 3445718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3455718399fSFrançois Tigeot zone = glob->zones[i]; 3460bece63dSImre Vadasz pr_info("Zone %7s: Available graphics memory: %llu kiB\n", 3475718399fSFrançois Tigeot zone->name, (unsigned long long)zone->max_mem >> 10); 3485718399fSFrançois Tigeot } 3495718399fSFrançois Tigeot ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 3505718399fSFrançois Tigeot ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 3515718399fSFrançois Tigeot return 0; 3525718399fSFrançois Tigeot out_no_zone: 3535718399fSFrançois Tigeot ttm_mem_global_release(glob); 3545718399fSFrançois Tigeot return ret; 3555718399fSFrançois Tigeot } 356c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_init); 3575718399fSFrançois Tigeot 3585718399fSFrançois Tigeot void ttm_mem_global_release(struct ttm_mem_global *glob) 3595718399fSFrançois Tigeot { 3605718399fSFrançois Tigeot unsigned int i; 3615718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3625718399fSFrançois Tigeot 3635718399fSFrançois Tigeot /* let the page allocator first stop the shrink work. */ 3645718399fSFrançois Tigeot ttm_page_alloc_fini(); 3655718399fSFrançois Tigeot ttm_dma_page_alloc_fini(); 3665718399fSFrançois Tigeot 3675718399fSFrançois Tigeot taskqueue_drain(glob->swap_queue, &glob->work); 3685718399fSFrançois Tigeot taskqueue_free(glob->swap_queue); 3695718399fSFrançois Tigeot glob->swap_queue = NULL; 3705718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3715718399fSFrançois Tigeot zone = glob->zones[i]; 372*3a2096e8SFrançois Tigeot kobject_del(&zone->kobj); 373*3a2096e8SFrançois Tigeot kobject_put(&zone->kobj); 3745718399fSFrançois Tigeot } 375*3a2096e8SFrançois Tigeot kobject_del(&glob->kobj); 376*3a2096e8SFrançois Tigeot kobject_put(&glob->kobj); 377*3a2096e8SFrançois Tigeot 3785718399fSFrançois Tigeot } 379c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_release); 3805718399fSFrançois Tigeot 3815718399fSFrançois Tigeot static void ttm_check_swapping(struct ttm_mem_global *glob) 3825718399fSFrançois Tigeot { 3835718399fSFrançois Tigeot bool needs_swapping = false; 3845718399fSFrançois Tigeot unsigned int i; 3855718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3865718399fSFrançois Tigeot 3875718399fSFrançois Tigeot spin_lock(&glob->spin); 3885718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3895718399fSFrançois Tigeot zone = glob->zones[i]; 3905718399fSFrançois Tigeot if (zone->used_mem > zone->swap_limit) { 3915718399fSFrançois Tigeot needs_swapping = true; 3925718399fSFrançois Tigeot break; 3935718399fSFrançois Tigeot } 3945718399fSFrançois Tigeot } 3955718399fSFrançois Tigeot spin_unlock(&glob->spin); 3965718399fSFrançois Tigeot 3975718399fSFrançois Tigeot if (unlikely(needs_swapping)) 3985718399fSFrançois Tigeot taskqueue_enqueue(glob->swap_queue, &glob->work); 3995718399fSFrançois Tigeot 4005718399fSFrançois Tigeot } 4015718399fSFrançois Tigeot 4025718399fSFrançois Tigeot static void ttm_mem_global_free_zone(struct ttm_mem_global *glob, 4035718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4045718399fSFrançois Tigeot uint64_t amount) 4055718399fSFrançois Tigeot { 4065718399fSFrançois Tigeot unsigned int i; 4075718399fSFrançois Tigeot struct ttm_mem_zone *zone; 4085718399fSFrançois Tigeot 4095718399fSFrançois Tigeot spin_lock(&glob->spin); 4105718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4115718399fSFrançois Tigeot zone = glob->zones[i]; 4125718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4135718399fSFrançois Tigeot continue; 4145718399fSFrançois Tigeot zone->used_mem -= amount; 4155718399fSFrançois Tigeot } 4165718399fSFrançois Tigeot spin_unlock(&glob->spin); 4175718399fSFrançois Tigeot } 4185718399fSFrançois Tigeot 4195718399fSFrançois Tigeot void ttm_mem_global_free(struct ttm_mem_global *glob, 4205718399fSFrançois Tigeot uint64_t amount) 4215718399fSFrançois Tigeot { 422111e70c7SSascha Wildner ttm_mem_global_free_zone(glob, NULL, amount); 4235718399fSFrançois Tigeot } 424c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_free); 4255718399fSFrançois Tigeot 4265718399fSFrançois Tigeot static int ttm_mem_global_reserve(struct ttm_mem_global *glob, 4275718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4285718399fSFrançois Tigeot uint64_t amount, bool reserve) 4295718399fSFrançois Tigeot { 4305718399fSFrançois Tigeot uint64_t limit; 4315718399fSFrançois Tigeot int ret = -ENOMEM; 4325718399fSFrançois Tigeot unsigned int i; 4335718399fSFrançois Tigeot struct ttm_mem_zone *zone; 4345718399fSFrançois Tigeot 4355718399fSFrançois Tigeot spin_lock(&glob->spin); 4365718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4375718399fSFrançois Tigeot zone = glob->zones[i]; 4385718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4395718399fSFrançois Tigeot continue; 4405718399fSFrançois Tigeot 4415718399fSFrançois Tigeot limit = (priv_check(curthread, PRIV_VM_MLOCK) == 0) ? 4425718399fSFrançois Tigeot zone->emer_mem : zone->max_mem; 4435718399fSFrançois Tigeot 4445718399fSFrançois Tigeot if (zone->used_mem > limit) 4455718399fSFrançois Tigeot goto out_unlock; 4465718399fSFrançois Tigeot } 4475718399fSFrançois Tigeot 4485718399fSFrançois Tigeot if (reserve) { 4495718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 4505718399fSFrançois Tigeot zone = glob->zones[i]; 4515718399fSFrançois Tigeot if (single_zone && zone != single_zone) 4525718399fSFrançois Tigeot continue; 4535718399fSFrançois Tigeot zone->used_mem += amount; 4545718399fSFrançois Tigeot } 4555718399fSFrançois Tigeot } 4565718399fSFrançois Tigeot 4575718399fSFrançois Tigeot ret = 0; 4585718399fSFrançois Tigeot out_unlock: 4595718399fSFrançois Tigeot spin_unlock(&glob->spin); 4605718399fSFrançois Tigeot ttm_check_swapping(glob); 4615718399fSFrançois Tigeot 4625718399fSFrançois Tigeot return ret; 4635718399fSFrançois Tigeot } 4645718399fSFrançois Tigeot 4655718399fSFrançois Tigeot 4665718399fSFrançois Tigeot static int ttm_mem_global_alloc_zone(struct ttm_mem_global *glob, 4675718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4685718399fSFrançois Tigeot uint64_t memory, 4695718399fSFrançois Tigeot bool no_wait, bool interruptible) 4705718399fSFrançois Tigeot { 4715718399fSFrançois Tigeot int count = TTM_MEMORY_ALLOC_RETRIES; 4725718399fSFrançois Tigeot 4735718399fSFrançois Tigeot while (unlikely(ttm_mem_global_reserve(glob, 4745718399fSFrançois Tigeot single_zone, 4755718399fSFrançois Tigeot memory, true) 4765718399fSFrançois Tigeot != 0)) { 4775718399fSFrançois Tigeot if (no_wait) 4785718399fSFrançois Tigeot return -ENOMEM; 4795718399fSFrançois Tigeot if (unlikely(count-- == 0)) 4805718399fSFrançois Tigeot return -ENOMEM; 4815718399fSFrançois Tigeot ttm_shrink(glob, false, memory + (memory >> 2) + 16); 4825718399fSFrançois Tigeot } 4835718399fSFrançois Tigeot 4845718399fSFrançois Tigeot return 0; 4855718399fSFrançois Tigeot } 4865718399fSFrançois Tigeot 4875718399fSFrançois Tigeot int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, 4885718399fSFrançois Tigeot bool no_wait, bool interruptible) 4895718399fSFrançois Tigeot { 4905718399fSFrançois Tigeot /** 4915718399fSFrançois Tigeot * Normal allocations of kernel memory are registered in 4925718399fSFrançois Tigeot * all zones. 4935718399fSFrançois Tigeot */ 4945718399fSFrançois Tigeot 4955718399fSFrançois Tigeot return ttm_mem_global_alloc_zone(glob, NULL, memory, no_wait, 4965718399fSFrançois Tigeot interruptible); 4975718399fSFrançois Tigeot } 498c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_alloc); 4995718399fSFrançois Tigeot 5005718399fSFrançois Tigeot int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, 501f0bba3d1SFrançois Tigeot struct page *page, 5025718399fSFrançois Tigeot bool no_wait, bool interruptible) 5035718399fSFrançois Tigeot { 5045718399fSFrançois Tigeot 5055718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 5065718399fSFrançois Tigeot 5075718399fSFrançois Tigeot /** 5085718399fSFrançois Tigeot * Page allocations may be registed in a single zone 5095718399fSFrançois Tigeot * only if highmem or !dma32. 5105718399fSFrançois Tigeot */ 5115718399fSFrançois Tigeot 5125718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 5135718399fSFrançois Tigeot zone = glob->zone_kernel; 5145718399fSFrançois Tigeot return ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, no_wait, 5155718399fSFrançois Tigeot interruptible); 5165718399fSFrançois Tigeot } 5175718399fSFrançois Tigeot 518f0bba3d1SFrançois Tigeot void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page) 5195718399fSFrançois Tigeot { 5205718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 5215718399fSFrançois Tigeot 5225718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 5235718399fSFrançois Tigeot zone = glob->zone_kernel; 5245718399fSFrançois Tigeot ttm_mem_global_free_zone(glob, zone, PAGE_SIZE); 5255718399fSFrançois Tigeot } 5265718399fSFrançois Tigeot 5275718399fSFrançois Tigeot 5285718399fSFrançois Tigeot size_t ttm_round_pot(size_t size) 5295718399fSFrançois Tigeot { 5305718399fSFrançois Tigeot if ((size & (size - 1)) == 0) 5315718399fSFrançois Tigeot return size; 5325718399fSFrançois Tigeot else if (size > PAGE_SIZE) 5335718399fSFrançois Tigeot return PAGE_ALIGN(size); 5345718399fSFrançois Tigeot else { 5355718399fSFrançois Tigeot size_t tmp_size = 4; 5365718399fSFrançois Tigeot 5375718399fSFrançois Tigeot while (tmp_size < size) 5385718399fSFrançois Tigeot tmp_size <<= 1; 5395718399fSFrançois Tigeot 5405718399fSFrançois Tigeot return tmp_size; 5415718399fSFrançois Tigeot } 5425718399fSFrançois Tigeot return 0; 5435718399fSFrançois Tigeot } 544c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_round_pot); 545