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 { 405718399fSFrançois Tigeot u_int kobj_ref; 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 505718399fSFrançois Tigeot static void ttm_mem_zone_kobj_release(struct ttm_mem_zone *zone) 515718399fSFrançois Tigeot { 525718399fSFrançois Tigeot 530bece63dSImre Vadasz pr_info("Zone %7s: Used memory at exit: %llu kiB\n", 545718399fSFrançois Tigeot zone->name, (unsigned long long)zone->used_mem >> 10); 55*175896dfSzrj kfree(zone); 565718399fSFrançois Tigeot } 575718399fSFrançois Tigeot 585718399fSFrançois Tigeot #if 0 595718399fSFrançois Tigeot /* XXXKIB sysctl */ 605718399fSFrançois Tigeot static ssize_t ttm_mem_zone_show(struct ttm_mem_zone *zone; 615718399fSFrançois Tigeot struct attribute *attr, 625718399fSFrançois Tigeot char *buffer) 635718399fSFrançois Tigeot { 645718399fSFrançois Tigeot uint64_t val = 0; 655718399fSFrançois Tigeot 665718399fSFrançois Tigeot mtx_lock(&zone->glob->lock); 675718399fSFrançois Tigeot if (attr == &ttm_mem_sys) 685718399fSFrançois Tigeot val = zone->zone_mem; 695718399fSFrançois Tigeot else if (attr == &ttm_mem_emer) 705718399fSFrançois Tigeot val = zone->emer_mem; 715718399fSFrançois Tigeot else if (attr == &ttm_mem_max) 725718399fSFrançois Tigeot val = zone->max_mem; 735718399fSFrançois Tigeot else if (attr == &ttm_mem_swap) 745718399fSFrançois Tigeot val = zone->swap_limit; 755718399fSFrançois Tigeot else if (attr == &ttm_mem_used) 765718399fSFrançois Tigeot val = zone->used_mem; 775718399fSFrançois Tigeot mtx_unlock(&zone->glob->lock); 785718399fSFrançois Tigeot 795718399fSFrançois Tigeot return snprintf(buffer, PAGE_SIZE, "%llu\n", 805718399fSFrançois Tigeot (unsigned long long) val >> 10); 815718399fSFrançois Tigeot } 825718399fSFrançois Tigeot #endif 835718399fSFrançois Tigeot 845718399fSFrançois Tigeot static void ttm_check_swapping(struct ttm_mem_global *glob); 855718399fSFrançois Tigeot 865718399fSFrançois Tigeot #if 0 875718399fSFrançois Tigeot /* XXXKIB sysctl */ 885718399fSFrançois Tigeot static ssize_t ttm_mem_zone_store(struct ttm_mem_zone *zone, 895718399fSFrançois Tigeot struct attribute *attr, 905718399fSFrançois Tigeot const char *buffer, 915718399fSFrançois Tigeot size_t size) 925718399fSFrançois Tigeot { 935718399fSFrançois Tigeot int chars; 945718399fSFrançois Tigeot unsigned long val; 955718399fSFrançois Tigeot uint64_t val64; 965718399fSFrançois Tigeot 975718399fSFrançois Tigeot chars = sscanf(buffer, "%lu", &val); 985718399fSFrançois Tigeot if (chars == 0) 995718399fSFrançois Tigeot return size; 1005718399fSFrançois Tigeot 1015718399fSFrançois Tigeot val64 = val; 1025718399fSFrançois Tigeot val64 <<= 10; 1035718399fSFrançois Tigeot 1045718399fSFrançois Tigeot mtx_lock(&zone->glob->lock); 1055718399fSFrançois Tigeot if (val64 > zone->zone_mem) 1065718399fSFrançois Tigeot val64 = zone->zone_mem; 1075718399fSFrançois Tigeot if (attr == &ttm_mem_emer) { 1085718399fSFrançois Tigeot zone->emer_mem = val64; 1095718399fSFrançois Tigeot if (zone->max_mem > val64) 1105718399fSFrançois Tigeot zone->max_mem = val64; 1115718399fSFrançois Tigeot } else if (attr == &ttm_mem_max) { 1125718399fSFrançois Tigeot zone->max_mem = val64; 1135718399fSFrançois Tigeot if (zone->emer_mem < val64) 1145718399fSFrançois Tigeot zone->emer_mem = val64; 1155718399fSFrançois Tigeot } else if (attr == &ttm_mem_swap) 1165718399fSFrançois Tigeot zone->swap_limit = val64; 1175718399fSFrançois Tigeot mtx_unlock(&zone->glob->lock); 1185718399fSFrançois Tigeot 1195718399fSFrançois Tigeot ttm_check_swapping(zone->glob); 1205718399fSFrançois Tigeot 1215718399fSFrançois Tigeot return size; 1225718399fSFrançois Tigeot } 1235718399fSFrançois Tigeot #endif 1245718399fSFrançois Tigeot 1255718399fSFrançois Tigeot static void ttm_mem_global_kobj_release(struct ttm_mem_global *glob) 1265718399fSFrançois Tigeot { 1275718399fSFrançois Tigeot } 1285718399fSFrançois Tigeot 1295718399fSFrançois Tigeot static bool ttm_zones_above_swap_target(struct ttm_mem_global *glob, 1305718399fSFrançois Tigeot bool from_wq, uint64_t extra) 1315718399fSFrançois Tigeot { 1325718399fSFrançois Tigeot unsigned int i; 1335718399fSFrançois Tigeot struct ttm_mem_zone *zone; 1345718399fSFrançois Tigeot uint64_t target; 1355718399fSFrançois Tigeot 1365718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 1375718399fSFrançois Tigeot zone = glob->zones[i]; 1385718399fSFrançois Tigeot 1395718399fSFrançois Tigeot if (from_wq) 1405718399fSFrançois Tigeot target = zone->swap_limit; 1415718399fSFrançois Tigeot else if (priv_check(curthread, PRIV_VM_MLOCK) == 0) 1425718399fSFrançois Tigeot target = zone->emer_mem; 1435718399fSFrançois Tigeot else 1445718399fSFrançois Tigeot target = zone->max_mem; 1455718399fSFrançois Tigeot 1465718399fSFrançois Tigeot target = (extra > target) ? 0ULL : target; 1475718399fSFrançois Tigeot 1485718399fSFrançois Tigeot if (zone->used_mem > target) 1495718399fSFrançois Tigeot return true; 1505718399fSFrançois Tigeot } 1515718399fSFrançois Tigeot return false; 1525718399fSFrançois Tigeot } 1535718399fSFrançois Tigeot 1545718399fSFrançois Tigeot /** 1555718399fSFrançois Tigeot * At this point we only support a single shrink callback. 1565718399fSFrançois Tigeot * Extend this if needed, perhaps using a linked list of callbacks. 1575718399fSFrançois Tigeot * Note that this function is reentrant: 1585718399fSFrançois Tigeot * many threads may try to swap out at any given time. 1595718399fSFrançois Tigeot */ 1605718399fSFrançois Tigeot 1615718399fSFrançois Tigeot static void ttm_shrink(struct ttm_mem_global *glob, bool from_wq, 1625718399fSFrançois Tigeot uint64_t extra) 1635718399fSFrançois Tigeot { 1645718399fSFrançois Tigeot int ret; 1655718399fSFrançois Tigeot struct ttm_mem_shrink *shrink; 1665718399fSFrançois Tigeot 1675718399fSFrançois Tigeot spin_lock(&glob->spin); 1685718399fSFrançois Tigeot if (glob->shrink == NULL) 1695718399fSFrançois Tigeot goto out; 1705718399fSFrançois Tigeot 1715718399fSFrançois Tigeot while (ttm_zones_above_swap_target(glob, from_wq, extra)) { 1725718399fSFrançois Tigeot shrink = glob->shrink; 1735718399fSFrançois Tigeot spin_unlock(&glob->spin); 174f6201ebfSMatthew Dillon ret = shrink->do_shrink(shrink); 175f6201ebfSMatthew Dillon spin_lock(&glob->spin); 1765718399fSFrançois Tigeot if (unlikely(ret != 0)) 1775718399fSFrançois Tigeot goto out; 1785718399fSFrançois Tigeot } 1795718399fSFrançois Tigeot out: 1805718399fSFrançois Tigeot spin_unlock(&glob->spin); 1815718399fSFrançois Tigeot } 1825718399fSFrançois Tigeot 1835718399fSFrançois Tigeot 1845718399fSFrançois Tigeot 1855718399fSFrançois Tigeot static void ttm_shrink_work(void *arg, int pending __unused) 1865718399fSFrançois Tigeot { 1875718399fSFrançois Tigeot struct ttm_mem_global *glob = arg; 1885718399fSFrançois Tigeot 1895718399fSFrançois Tigeot ttm_shrink(glob, true, 0ULL); 1905718399fSFrançois Tigeot } 1915718399fSFrançois Tigeot 1925718399fSFrançois Tigeot static int ttm_mem_init_kernel_zone(struct ttm_mem_global *glob, 1935718399fSFrançois Tigeot uint64_t mem) 1945718399fSFrançois Tigeot { 1955718399fSFrançois Tigeot struct ttm_mem_zone *zone; 1965718399fSFrançois Tigeot 197*175896dfSzrj zone = kzalloc(sizeof(*zone), GFP_KERNEL); 1985718399fSFrançois Tigeot 1995718399fSFrançois Tigeot zone->name = "kernel"; 2005718399fSFrançois Tigeot zone->zone_mem = mem; 2015718399fSFrançois Tigeot zone->max_mem = mem >> 1; 2025718399fSFrançois Tigeot zone->emer_mem = (mem >> 1) + (mem >> 2); 2035718399fSFrançois Tigeot zone->swap_limit = zone->max_mem - (mem >> 3); 2045718399fSFrançois Tigeot zone->used_mem = 0; 2055718399fSFrançois Tigeot zone->glob = glob; 2065718399fSFrançois Tigeot glob->zone_kernel = zone; 2075718399fSFrançois Tigeot refcount_init(&zone->kobj_ref, 1); 2085718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 2095718399fSFrançois Tigeot return 0; 2105718399fSFrançois Tigeot } 2115718399fSFrançois Tigeot 2125718399fSFrançois Tigeot static int ttm_mem_init_dma32_zone(struct ttm_mem_global *glob, 2135718399fSFrançois Tigeot uint64_t mem) 2145718399fSFrançois Tigeot { 2155718399fSFrançois Tigeot struct ttm_mem_zone *zone; 2165718399fSFrançois Tigeot 217*175896dfSzrj zone = kzalloc(sizeof(*zone), GFP_KERNEL); 2185718399fSFrançois Tigeot 2195718399fSFrançois Tigeot /** 2205718399fSFrançois Tigeot * No special dma32 zone needed. 2215718399fSFrançois Tigeot */ 2225718399fSFrançois Tigeot 223f6201ebfSMatthew Dillon if ((physmem * PAGE_SIZE) <= ((uint64_t) 1ULL << 32)) { 224*175896dfSzrj kfree(zone); 2255718399fSFrançois Tigeot return 0; 2265718399fSFrançois Tigeot } 2275718399fSFrançois Tigeot 2285718399fSFrançois Tigeot /* 2295718399fSFrançois Tigeot * Limit max dma32 memory to 4GB for now 2305718399fSFrançois Tigeot * until we can figure out how big this 2315718399fSFrançois Tigeot * zone really is. 2325718399fSFrançois Tigeot */ 233f6201ebfSMatthew Dillon if (mem > ((uint64_t) 1ULL << 32)) 2345718399fSFrançois Tigeot mem = ((uint64_t) 1ULL << 32); 235f6201ebfSMatthew Dillon 2365718399fSFrançois Tigeot zone->name = "dma32"; 2375718399fSFrançois Tigeot zone->zone_mem = mem; 2385718399fSFrançois Tigeot zone->max_mem = mem >> 1; 2395718399fSFrançois Tigeot zone->emer_mem = (mem >> 1) + (mem >> 2); 2405718399fSFrançois Tigeot zone->swap_limit = zone->max_mem - (mem >> 3); 2415718399fSFrançois Tigeot zone->used_mem = 0; 2425718399fSFrançois Tigeot zone->glob = glob; 2435718399fSFrançois Tigeot glob->zone_dma32 = zone; 2445718399fSFrançois Tigeot refcount_init(&zone->kobj_ref, 1); 2455718399fSFrançois Tigeot glob->zones[glob->num_zones++] = zone; 2465718399fSFrançois Tigeot return 0; 2475718399fSFrançois Tigeot } 2485718399fSFrançois Tigeot 2495718399fSFrançois Tigeot int ttm_mem_global_init(struct ttm_mem_global *glob) 2505718399fSFrançois Tigeot { 2515718399fSFrançois Tigeot u_int64_t mem; 2525718399fSFrançois Tigeot int ret; 2535718399fSFrançois Tigeot int i; 2545718399fSFrançois Tigeot struct ttm_mem_zone *zone; 2555718399fSFrançois Tigeot 256ba87a4abSSascha Wildner spin_init(&glob->spin, "ttmemglob"); 2575718399fSFrançois Tigeot glob->swap_queue = taskqueue_create("ttm_swap", M_WAITOK, 2585718399fSFrançois Tigeot taskqueue_thread_enqueue, &glob->swap_queue); 2595718399fSFrançois Tigeot taskqueue_start_threads(&glob->swap_queue, 1, 0, -1, "ttm swap"); 2605718399fSFrançois Tigeot TASK_INIT(&glob->work, 0, ttm_shrink_work, glob); 2615718399fSFrançois Tigeot 2625718399fSFrançois Tigeot refcount_init(&glob->kobj_ref, 1); 2635718399fSFrançois Tigeot 264f6201ebfSMatthew Dillon /* 265f6201ebfSMatthew Dillon * Managed contiguous memory for TTM. Only use kernel-reserved 266f6201ebfSMatthew Dillon * dma memory for TTM, which can be controlled via /boot/loader.conf 267f6201ebfSMatthew Dillon * (e.g. vm.dma_reserved=256m). This is the only truly dependable 268f6201ebfSMatthew Dillon * DMA memory. 269f6201ebfSMatthew Dillon */ 270f6201ebfSMatthew Dillon mem = (uint64_t)vm_contig_avail_pages() * PAGE_SIZE; 2715718399fSFrançois Tigeot 2725718399fSFrançois Tigeot ret = ttm_mem_init_kernel_zone(glob, mem); 2735718399fSFrançois Tigeot if (unlikely(ret != 0)) 2745718399fSFrançois Tigeot goto out_no_zone; 2755718399fSFrançois Tigeot ret = ttm_mem_init_dma32_zone(glob, mem); 2765718399fSFrançois Tigeot if (unlikely(ret != 0)) 2775718399fSFrançois Tigeot goto out_no_zone; 2780bece63dSImre Vadasz pr_info("(struct ttm_mem_global *)%p\n", glob); 2795718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 2805718399fSFrançois Tigeot zone = glob->zones[i]; 2810bece63dSImre Vadasz pr_info("Zone %7s: Available graphics memory: %llu kiB\n", 2825718399fSFrançois Tigeot zone->name, (unsigned long long)zone->max_mem >> 10); 2835718399fSFrançois Tigeot } 2845718399fSFrançois Tigeot ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 2855718399fSFrançois Tigeot ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE)); 2865718399fSFrançois Tigeot return 0; 2875718399fSFrançois Tigeot out_no_zone: 2885718399fSFrançois Tigeot ttm_mem_global_release(glob); 2895718399fSFrançois Tigeot return ret; 2905718399fSFrançois Tigeot } 291c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_init); 2925718399fSFrançois Tigeot 2935718399fSFrançois Tigeot void ttm_mem_global_release(struct ttm_mem_global *glob) 2945718399fSFrançois Tigeot { 2955718399fSFrançois Tigeot unsigned int i; 2965718399fSFrançois Tigeot struct ttm_mem_zone *zone; 2975718399fSFrançois Tigeot 2985718399fSFrançois Tigeot /* let the page allocator first stop the shrink work. */ 2995718399fSFrançois Tigeot ttm_page_alloc_fini(); 3005718399fSFrançois Tigeot ttm_dma_page_alloc_fini(); 3015718399fSFrançois Tigeot 3025718399fSFrançois Tigeot taskqueue_drain(glob->swap_queue, &glob->work); 3035718399fSFrançois Tigeot taskqueue_free(glob->swap_queue); 3045718399fSFrançois Tigeot glob->swap_queue = NULL; 3055718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3065718399fSFrançois Tigeot zone = glob->zones[i]; 3075718399fSFrançois Tigeot if (refcount_release(&zone->kobj_ref)) 3085718399fSFrançois Tigeot ttm_mem_zone_kobj_release(zone); 3095718399fSFrançois Tigeot } 3105718399fSFrançois Tigeot if (refcount_release(&glob->kobj_ref)) 3115718399fSFrançois Tigeot ttm_mem_global_kobj_release(glob); 3125718399fSFrançois Tigeot } 313c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_release); 3145718399fSFrançois Tigeot 3155718399fSFrançois Tigeot static void ttm_check_swapping(struct ttm_mem_global *glob) 3165718399fSFrançois Tigeot { 3175718399fSFrançois Tigeot bool needs_swapping = false; 3185718399fSFrançois Tigeot unsigned int i; 3195718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3205718399fSFrançois Tigeot 3215718399fSFrançois Tigeot spin_lock(&glob->spin); 3225718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3235718399fSFrançois Tigeot zone = glob->zones[i]; 3245718399fSFrançois Tigeot if (zone->used_mem > zone->swap_limit) { 3255718399fSFrançois Tigeot needs_swapping = true; 3265718399fSFrançois Tigeot break; 3275718399fSFrançois Tigeot } 3285718399fSFrançois Tigeot } 3295718399fSFrançois Tigeot spin_unlock(&glob->spin); 3305718399fSFrançois Tigeot 3315718399fSFrançois Tigeot if (unlikely(needs_swapping)) 3325718399fSFrançois Tigeot taskqueue_enqueue(glob->swap_queue, &glob->work); 3335718399fSFrançois Tigeot 3345718399fSFrançois Tigeot } 3355718399fSFrançois Tigeot 3365718399fSFrançois Tigeot static void ttm_mem_global_free_zone(struct ttm_mem_global *glob, 3375718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 3385718399fSFrançois Tigeot uint64_t amount) 3395718399fSFrançois Tigeot { 3405718399fSFrançois Tigeot unsigned int i; 3415718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3425718399fSFrançois Tigeot 3435718399fSFrançois Tigeot spin_lock(&glob->spin); 3445718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3455718399fSFrançois Tigeot zone = glob->zones[i]; 3465718399fSFrançois Tigeot if (single_zone && zone != single_zone) 3475718399fSFrançois Tigeot continue; 3485718399fSFrançois Tigeot zone->used_mem -= amount; 3495718399fSFrançois Tigeot } 3505718399fSFrançois Tigeot spin_unlock(&glob->spin); 3515718399fSFrançois Tigeot } 3525718399fSFrançois Tigeot 3535718399fSFrançois Tigeot void ttm_mem_global_free(struct ttm_mem_global *glob, 3545718399fSFrançois Tigeot uint64_t amount) 3555718399fSFrançois Tigeot { 356111e70c7SSascha Wildner ttm_mem_global_free_zone(glob, NULL, amount); 3575718399fSFrançois Tigeot } 358c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_free); 3595718399fSFrançois Tigeot 3605718399fSFrançois Tigeot static int ttm_mem_global_reserve(struct ttm_mem_global *glob, 3615718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 3625718399fSFrançois Tigeot uint64_t amount, bool reserve) 3635718399fSFrançois Tigeot { 3645718399fSFrançois Tigeot uint64_t limit; 3655718399fSFrançois Tigeot int ret = -ENOMEM; 3665718399fSFrançois Tigeot unsigned int i; 3675718399fSFrançois Tigeot struct ttm_mem_zone *zone; 3685718399fSFrançois Tigeot 3695718399fSFrançois Tigeot spin_lock(&glob->spin); 3705718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3715718399fSFrançois Tigeot zone = glob->zones[i]; 3725718399fSFrançois Tigeot if (single_zone && zone != single_zone) 3735718399fSFrançois Tigeot continue; 3745718399fSFrançois Tigeot 3755718399fSFrançois Tigeot limit = (priv_check(curthread, PRIV_VM_MLOCK) == 0) ? 3765718399fSFrançois Tigeot zone->emer_mem : zone->max_mem; 3775718399fSFrançois Tigeot 3785718399fSFrançois Tigeot if (zone->used_mem > limit) 3795718399fSFrançois Tigeot goto out_unlock; 3805718399fSFrançois Tigeot } 3815718399fSFrançois Tigeot 3825718399fSFrançois Tigeot if (reserve) { 3835718399fSFrançois Tigeot for (i = 0; i < glob->num_zones; ++i) { 3845718399fSFrançois Tigeot zone = glob->zones[i]; 3855718399fSFrançois Tigeot if (single_zone && zone != single_zone) 3865718399fSFrançois Tigeot continue; 3875718399fSFrançois Tigeot zone->used_mem += amount; 3885718399fSFrançois Tigeot } 3895718399fSFrançois Tigeot } 3905718399fSFrançois Tigeot 3915718399fSFrançois Tigeot ret = 0; 3925718399fSFrançois Tigeot out_unlock: 3935718399fSFrançois Tigeot spin_unlock(&glob->spin); 3945718399fSFrançois Tigeot ttm_check_swapping(glob); 3955718399fSFrançois Tigeot 3965718399fSFrançois Tigeot return ret; 3975718399fSFrançois Tigeot } 3985718399fSFrançois Tigeot 3995718399fSFrançois Tigeot 4005718399fSFrançois Tigeot static int ttm_mem_global_alloc_zone(struct ttm_mem_global *glob, 4015718399fSFrançois Tigeot struct ttm_mem_zone *single_zone, 4025718399fSFrançois Tigeot uint64_t memory, 4035718399fSFrançois Tigeot bool no_wait, bool interruptible) 4045718399fSFrançois Tigeot { 4055718399fSFrançois Tigeot int count = TTM_MEMORY_ALLOC_RETRIES; 4065718399fSFrançois Tigeot 4075718399fSFrançois Tigeot while (unlikely(ttm_mem_global_reserve(glob, 4085718399fSFrançois Tigeot single_zone, 4095718399fSFrançois Tigeot memory, true) 4105718399fSFrançois Tigeot != 0)) { 4115718399fSFrançois Tigeot if (no_wait) 4125718399fSFrançois Tigeot return -ENOMEM; 4135718399fSFrançois Tigeot if (unlikely(count-- == 0)) 4145718399fSFrançois Tigeot return -ENOMEM; 4155718399fSFrançois Tigeot ttm_shrink(glob, false, memory + (memory >> 2) + 16); 4165718399fSFrançois Tigeot } 4175718399fSFrançois Tigeot 4185718399fSFrançois Tigeot return 0; 4195718399fSFrançois Tigeot } 4205718399fSFrançois Tigeot 4215718399fSFrançois Tigeot int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, 4225718399fSFrançois Tigeot bool no_wait, bool interruptible) 4235718399fSFrançois Tigeot { 4245718399fSFrançois Tigeot /** 4255718399fSFrançois Tigeot * Normal allocations of kernel memory are registered in 4265718399fSFrançois Tigeot * all zones. 4275718399fSFrançois Tigeot */ 4285718399fSFrançois Tigeot 4295718399fSFrançois Tigeot return ttm_mem_global_alloc_zone(glob, NULL, memory, no_wait, 4305718399fSFrançois Tigeot interruptible); 4315718399fSFrançois Tigeot } 432c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_mem_global_alloc); 4335718399fSFrançois Tigeot 4345718399fSFrançois Tigeot int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, 4355718399fSFrançois Tigeot struct vm_page *page, 4365718399fSFrançois Tigeot bool no_wait, bool interruptible) 4375718399fSFrançois Tigeot { 4385718399fSFrançois Tigeot 4395718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 4405718399fSFrançois Tigeot 4415718399fSFrançois Tigeot /** 4425718399fSFrançois Tigeot * Page allocations may be registed in a single zone 4435718399fSFrançois Tigeot * only if highmem or !dma32. 4445718399fSFrançois Tigeot */ 4455718399fSFrançois Tigeot 4465718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 4475718399fSFrançois Tigeot zone = glob->zone_kernel; 4485718399fSFrançois Tigeot return ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, no_wait, 4495718399fSFrançois Tigeot interruptible); 4505718399fSFrançois Tigeot } 4515718399fSFrançois Tigeot 4525718399fSFrançois Tigeot void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct vm_page *page) 4535718399fSFrançois Tigeot { 4545718399fSFrançois Tigeot struct ttm_mem_zone *zone = NULL; 4555718399fSFrançois Tigeot 4565718399fSFrançois Tigeot if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) 4575718399fSFrançois Tigeot zone = glob->zone_kernel; 4585718399fSFrançois Tigeot ttm_mem_global_free_zone(glob, zone, PAGE_SIZE); 4595718399fSFrançois Tigeot } 4605718399fSFrançois Tigeot 4615718399fSFrançois Tigeot 4625718399fSFrançois Tigeot size_t ttm_round_pot(size_t size) 4635718399fSFrançois Tigeot { 4645718399fSFrançois Tigeot if ((size & (size - 1)) == 0) 4655718399fSFrançois Tigeot return size; 4665718399fSFrançois Tigeot else if (size > PAGE_SIZE) 4675718399fSFrançois Tigeot return PAGE_ALIGN(size); 4685718399fSFrançois Tigeot else { 4695718399fSFrançois Tigeot size_t tmp_size = 4; 4705718399fSFrançois Tigeot 4715718399fSFrançois Tigeot while (tmp_size < size) 4725718399fSFrançois Tigeot tmp_size <<= 1; 4735718399fSFrançois Tigeot 4745718399fSFrançois Tigeot return tmp_size; 4755718399fSFrançois Tigeot } 4765718399fSFrançois Tigeot return 0; 4775718399fSFrançois Tigeot } 478c19c6249SFrançois Tigeot EXPORT_SYMBOL(ttm_round_pot); 479