vmalloc.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:7k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/mm/vmalloc.c
  3.  *
  4.  *  Copyright (C) 1993  Linus Torvalds
  5.  *  Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999
  6.  *  SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian <tigran@veritas.com>, May 2000
  7.  */
  8. #include <linux/config.h>
  9. #include <linux/slab.h>
  10. #include <linux/vmalloc.h>
  11. #include <linux/spinlock.h>
  12. #include <linux/highmem.h>
  13. #include <linux/smp_lock.h>
  14. #include <asm/uaccess.h>
  15. #include <asm/pgalloc.h>
  16. rwlock_t vmlist_lock = RW_LOCK_UNLOCKED;
  17. struct vm_struct * vmlist;
  18. static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
  19. {
  20. pte_t * pte;
  21. unsigned long end;
  22. if (pmd_none(*pmd))
  23. return;
  24. if (pmd_bad(*pmd)) {
  25. pmd_ERROR(*pmd);
  26. pmd_clear(pmd);
  27. return;
  28. }
  29. pte = pte_offset(pmd, address);
  30. address &= ~PMD_MASK;
  31. end = address + size;
  32. if (end > PMD_SIZE)
  33. end = PMD_SIZE;
  34. do {
  35. pte_t page;
  36. page = ptep_get_and_clear(pte);
  37. address += PAGE_SIZE;
  38. pte++;
  39. if (pte_none(page))
  40. continue;
  41. if (pte_present(page)) {
  42. struct page *ptpage = pte_page(page);
  43. if (VALID_PAGE(ptpage) && (!PageReserved(ptpage)))
  44. __free_page(ptpage);
  45. continue;
  46. }
  47. printk(KERN_CRIT "Whee.. Swapped out page in kernel page tablen");
  48. } while (address < end);
  49. }
  50. static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
  51. {
  52. pmd_t * pmd;
  53. unsigned long end;
  54. if (pgd_none(*dir))
  55. return;
  56. if (pgd_bad(*dir)) {
  57. pgd_ERROR(*dir);
  58. pgd_clear(dir);
  59. return;
  60. }
  61. pmd = pmd_offset(dir, address);
  62. address &= ~PGDIR_MASK;
  63. end = address + size;
  64. if (end > PGDIR_SIZE)
  65. end = PGDIR_SIZE;
  66. do {
  67. free_area_pte(pmd, address, end - address);
  68. address = (address + PMD_SIZE) & PMD_MASK;
  69. pmd++;
  70. } while (address < end);
  71. }
  72. void vmfree_area_pages(unsigned long address, unsigned long size)
  73. {
  74. pgd_t * dir;
  75. unsigned long end = address + size;
  76. dir = pgd_offset_k(address);
  77. flush_cache_all();
  78. do {
  79. free_area_pmd(dir, address, end - address);
  80. address = (address + PGDIR_SIZE) & PGDIR_MASK;
  81. dir++;
  82. } while (address && (address < end));
  83. flush_tlb_all();
  84. }
  85. static inline int alloc_area_pte (pte_t * pte, unsigned long address,
  86. unsigned long size, int gfp_mask, pgprot_t prot)
  87. {
  88. unsigned long end;
  89. address &= ~PMD_MASK;
  90. end = address + size;
  91. if (end > PMD_SIZE)
  92. end = PMD_SIZE;
  93. do {
  94. struct page * page;
  95. spin_unlock(&init_mm.page_table_lock);
  96. page = alloc_page(gfp_mask);
  97. spin_lock(&init_mm.page_table_lock);
  98. if (!pte_none(*pte))
  99. printk(KERN_ERR "alloc_area_pte: page already existsn");
  100. if (!page)
  101. return -ENOMEM;
  102. set_pte(pte, mk_pte(page, prot));
  103. address += PAGE_SIZE;
  104. pte++;
  105. } while (address < end);
  106. return 0;
  107. }
  108. static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, int gfp_mask, pgprot_t prot)
  109. {
  110. unsigned long end;
  111. address &= ~PGDIR_MASK;
  112. end = address + size;
  113. if (end > PGDIR_SIZE)
  114. end = PGDIR_SIZE;
  115. do {
  116. pte_t * pte = pte_alloc(&init_mm, pmd, address);
  117. if (!pte)
  118. return -ENOMEM;
  119. if (alloc_area_pte(pte, address, end - address, gfp_mask, prot))
  120. return -ENOMEM;
  121. address = (address + PMD_SIZE) & PMD_MASK;
  122. pmd++;
  123. } while (address < end);
  124. return 0;
  125. }
  126. inline int vmalloc_area_pages (unsigned long address, unsigned long size,
  127.                                int gfp_mask, pgprot_t prot)
  128. {
  129. pgd_t * dir;
  130. unsigned long end = address + size;
  131. int ret;
  132. dir = pgd_offset_k(address);
  133. spin_lock(&init_mm.page_table_lock);
  134. do {
  135. pmd_t *pmd;
  136. pmd = pmd_alloc(&init_mm, dir, address);
  137. ret = -ENOMEM;
  138. if (!pmd)
  139. break;
  140. ret = -ENOMEM;
  141. if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot))
  142. break;
  143. address = (address + PGDIR_SIZE) & PGDIR_MASK;
  144. dir++;
  145. ret = 0;
  146. } while (address && (address < end));
  147. spin_unlock(&init_mm.page_table_lock);
  148. flush_cache_all();
  149. return ret;
  150. }
  151. struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)
  152. {
  153. unsigned long addr;
  154. struct vm_struct **p, *tmp, *area;
  155. area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
  156. if (!area)
  157. return NULL;
  158. size += PAGE_SIZE;
  159. #ifdef VMALLOC_ALIGN
  160. size = (size + VMALLOC_ALIGN - 1) & ~(VMALLOC_ALIGN - 1);
  161. #endif
  162. addr = VMALLOC_START;
  163. write_lock(&vmlist_lock);
  164. for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
  165. if ((size + addr) < addr)
  166. goto out;
  167. if (size + addr <= (unsigned long) tmp->addr)
  168. break;
  169. addr = tmp->size + (unsigned long) tmp->addr;
  170. if (addr > VMALLOC_END-size)
  171. goto out;
  172. }
  173. area->flags = flags;
  174. area->addr = (void *)addr;
  175. area->size = size;
  176. area->next = *p;
  177. *p = area;
  178. write_unlock(&vmlist_lock);
  179. return area;
  180. out:
  181. write_unlock(&vmlist_lock);
  182. kfree(area);
  183. return NULL;
  184. }
  185. void vfree(void * addr)
  186. {
  187. struct vm_struct **p, *tmp;
  188. if (!addr)
  189. return;
  190. if ((PAGE_SIZE-1) & (unsigned long) addr) {
  191. printk(KERN_ERR "Trying to vfree() bad address (%p)n", addr);
  192. return;
  193. }
  194. write_lock(&vmlist_lock);
  195. for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
  196. if (tmp->addr == addr) {
  197. *p = tmp->next;
  198. vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
  199. write_unlock(&vmlist_lock);
  200. kfree(tmp);
  201. return;
  202. }
  203. }
  204. write_unlock(&vmlist_lock);
  205. printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)n", addr);
  206. }
  207. void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot)
  208. {
  209. void * addr;
  210. struct vm_struct *area;
  211. size = PAGE_ALIGN(size);
  212. if (!size || (size >> PAGE_SHIFT) > num_physpages) {
  213. BUG();
  214. return NULL;
  215. }
  216. area = get_vm_area(size, VM_ALLOC);
  217. if (!area)
  218. return NULL;
  219. addr = area->addr;
  220. if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, gfp_mask, prot)) {
  221. vfree(addr);
  222. return NULL;
  223. }
  224. return addr;
  225. }
  226. long vread(char *buf, char *addr, unsigned long count)
  227. {
  228. struct vm_struct *tmp;
  229. char *vaddr, *buf_start = buf;
  230. unsigned long n;
  231. /* Don't allow overflow */
  232. if ((unsigned long) addr + count < count)
  233. count = -(unsigned long) addr;
  234. read_lock(&vmlist_lock);
  235. for (tmp = vmlist; tmp; tmp = tmp->next) {
  236. vaddr = (char *) tmp->addr;
  237. if (addr >= vaddr + tmp->size - PAGE_SIZE)
  238. continue;
  239. while (addr < vaddr) {
  240. if (count == 0)
  241. goto finished;
  242. *buf = '';
  243. buf++;
  244. addr++;
  245. count--;
  246. }
  247. n = vaddr + tmp->size - PAGE_SIZE - addr;
  248. do {
  249. if (count == 0)
  250. goto finished;
  251. *buf = *addr;
  252. buf++;
  253. addr++;
  254. count--;
  255. } while (--n > 0);
  256. }
  257. finished:
  258. read_unlock(&vmlist_lock);
  259. return buf - buf_start;
  260. }
  261. long vwrite(char *buf, char *addr, unsigned long count)
  262. {
  263. struct vm_struct *tmp;
  264. char *vaddr, *buf_start = buf;
  265. unsigned long n;
  266. /* Don't allow overflow */
  267. if ((unsigned long) addr + count < count)
  268. count = -(unsigned long) addr;
  269. read_lock(&vmlist_lock);
  270. for (tmp = vmlist; tmp; tmp = tmp->next) {
  271. vaddr = (char *) tmp->addr;
  272. if (addr >= vaddr + tmp->size - PAGE_SIZE)
  273. continue;
  274. while (addr < vaddr) {
  275. if (count == 0)
  276. goto finished;
  277. buf++;
  278. addr++;
  279. count--;
  280. }
  281. n = vaddr + tmp->size - PAGE_SIZE - addr;
  282. do {
  283. if (count == 0)
  284. goto finished;
  285. *addr = *buf;
  286. buf++;
  287. addr++;
  288. count--;
  289. } while (--n > 0);
  290. }
  291. finished:
  292. read_unlock(&vmlist_lock);
  293. return buf - buf_start;
  294. }