Advertisement
vtl

Huge contiguous physical memory allocation

vtl
Dec 14th, 2011
275
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 4.04 KB | None | 0 0
  1. /*
  2.  * nsmm_auto.c
  3.  *
  4.  * NovaSparks - Automatic DMA buffer allocation
  5.  *
  6.  * Author: V. Mayatskikh <[email protected]>
  7.  *
  8.  * Copyright (c) 2011 Novasparks
  9.  *
  10.  * All rights reserved
  11.  *
  12.  * This program is free software; you can redistribute it and/or modify
  13.  * it under the terms of the GNU General Public License version 2 as
  14.  * published by the Free Software Foundation.
  15.  */
  16.  
  17. #include <linux/mm.h>
  18. #include <linux/slab.h>
  19. #include <linux/sort.h>
  20. #include <linux/gfp.h>
  21. #include <asm/io.h>
  22.  
  23. #include <ns_debug.h>
  24. #include <nsmm.h>
  25. #include <nsmm_auto.h>
  26.  
  27. typedef struct {
  28.     unsigned long virt;
  29.     unsigned long phys;
  30. } mem_t;
  31.  
  32. static mem_t *pages;
  33. static int pages_allocated = 0;
  34.  
  35. static int mem_t_cmp(const void *a, const void *b)
  36. {
  37.     return (((mem_t *)a)->phys >= ((mem_t *)b)->phys) ? 0 : -1;
  38. }
  39.  
  40. static void mem_t_swap(void *a, void *b, int size)
  41. {
  42.     mem_t t = *(mem_t *)a;
  43.     *(mem_t *)a = *(mem_t *)b;
  44.     *(mem_t *)b = t;
  45. }
  46.  
  47. static void reserve_page10(unsigned long addr, int reserve)
  48. {
  49.     int i;
  50.  
  51.     /* Reserved pages are not compacted */
  52.     for (i = 0; i < (1ULL << 10); i++, addr += PAGE_SIZE) {
  53.         reserve ? SetPageReserved(virt_to_page(phys_to_virt(addr)))
  54.             : ClearPageReserved(virt_to_page(phys_to_virt(addr)));
  55.     }
  56. }
  57.  
  58. #define ALIGN_ON(size, x)                       \
  59.     (((size) % (x)) ? (size) + ((x) - ((size) % (x))) : (size))
  60.  
  61. #define ORDER_10_PAGE_SIZE (PAGE_SIZE * (1ULL << 10))
  62.  
  63. unsigned long find_largest_segment(unsigned long *base)
  64. {
  65.     unsigned long addr, start, seg_len;
  66.     unsigned long seg_base, seg_size;
  67.     int i;
  68.  
  69.     sort(pages, pages_allocated, sizeof(mem_t), mem_t_cmp, mem_t_swap);
  70.  
  71.     start = seg_base = pages[0].phys;
  72.     seg_size = seg_len = ORDER_10_PAGE_SIZE;
  73.  
  74.     for (i = 1; i < pages_allocated; i++) {
  75.         addr = pages[i].phys;
  76.         if (i == pages_allocated - 1 && /* last page */
  77.             start + seg_len == addr) {
  78.             seg_len += ORDER_10_PAGE_SIZE;
  79.             addr = (unsigned long)-1;
  80.         }
  81.         if (start + seg_len != addr) {
  82.             if (seg_size < seg_len) {
  83.                 seg_base = start;
  84.                 seg_size = seg_len;
  85.             }
  86.             start = addr;
  87.             seg_len = 0;
  88.         }
  89.         seg_len += ORDER_10_PAGE_SIZE;
  90.     }
  91.     *base = seg_base;
  92.  
  93.     return seg_size;
  94. }
  95.  
  96. bool nsmm_allocate_memory(unsigned long min, unsigned long max,
  97.               unsigned long *base, unsigned long *size)
  98. {
  99.     int i, max_pages = ALIGN_ON(max, ORDER_10_PAGE_SIZE) / ORDER_10_PAGE_SIZE;
  100.     unsigned long addr, seg_base, seg_size = 0;
  101.     int freed;
  102.  
  103.     ns_dbg(1, "trying to allocate up to %d order-10 pages", max_pages);
  104.     pages = kzalloc(max_pages * sizeof(mem_t), GFP_KERNEL);
  105.     if (!pages) {
  106.         ns_err("can't allocate space for pages");
  107.         return false;
  108.     }
  109.  
  110.     for (pages_allocated = 0, i = 0; i < max_pages; i++) {
  111.         addr = __get_free_pages(GFP_DMA32, 10);
  112.         if (!addr)
  113.             break;
  114.         pages_allocated++;
  115.         pages[i].virt = addr;
  116.         pages[i].phys = virt_to_phys((void*)addr);
  117.         seg_size = find_largest_segment(&seg_base);
  118.         if (seg_size >= min)
  119.             break;
  120.     }
  121.  
  122.     ns_dbg(1, "allocated %d order-10 pages", pages_allocated);
  123.  
  124.     if (!pages_allocated)
  125.         goto error;
  126.  
  127.     if (seg_size < min) {  /* if min can't be allocated */
  128.         ns_err("asked to allocate %lu bytes, could do only %lu",
  129.                min, seg_size);
  130.         goto error;
  131.     }
  132.  
  133.     /* keep the longest segment and free the rest*/
  134.     for (i = 0, freed = 0; i < pages_allocated; i++) {
  135.         if (pages[i].phys < seg_base ||
  136.             pages[i].phys >= seg_base + seg_size) {
  137.             free_pages(pages[i].virt, 10);
  138.             pages[i].phys = 0;
  139.             freed++;
  140.         } else {
  141.             reserve_page10(pages[i].phys, 1);
  142.         }
  143.     }
  144.     ns_dbg(1, "freed %d unused order-10 pages", freed);
  145.     *base = seg_base;
  146.     *size = seg_size;
  147.     ns_dbg(1, "found segment %p %lu mb", (void *)seg_base, (seg_size >> 20ULL));
  148.  
  149.     return true;
  150. error:
  151.     nsmm_free_memory();
  152.     return false;
  153. }
  154.  
  155. void nsmm_free_memory(void)
  156. {
  157.     int freed = 0, i;
  158.  
  159.     for (i = 0 ; i < pages_allocated; i++) {
  160.         if (pages[i].phys) {
  161.             reserve_page10(pages[i].phys, 0);
  162.             free_pages((unsigned long)phys_to_virt(pages[i].phys), 10);
  163.             freed++;
  164.         }
  165.     }
  166.     ns_dbg(1, "freed %d order-10 pages", freed);
  167.     if (pages) {
  168.         kfree(pages);
  169.         pages = NULL;
  170.     }
  171. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement