vmregion.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #include <linux/fs.h>
  2. #include <linux/spinlock.h>
  3. #include <linux/list.h>
  4. #include <linux/proc_fs.h>
  5. #include <linux/seq_file.h>
  6. #include <linux/slab.h>
  7. #include "vmregion.h"
  8. /*
  9. * VM region handling support.
  10. *
  11. * This should become something generic, handling VM region allocations for
  12. * vmalloc and similar (ioremap, module space, etc).
  13. *
  14. * I envisage vmalloc()'s supporting vm_struct becoming:
  15. *
  16. * struct vm_struct {
  17. * struct vmregion region;
  18. * unsigned long flags;
  19. * struct page **pages;
  20. * unsigned int nr_pages;
  21. * unsigned long phys_addr;
  22. * };
  23. *
  24. * get_vm_area() would then call vmregion_alloc with an appropriate
  25. * struct vmregion head (eg):
  26. *
  27. * struct vmregion vmalloc_head = {
  28. * .vm_list = LIST_HEAD_INIT(vmalloc_head.vm_list),
  29. * .vm_start = VMALLOC_START,
  30. * .vm_end = VMALLOC_END,
  31. * };
  32. *
  33. * However, vmalloc_head.vm_start is variable (typically, it is dependent on
  34. * the amount of RAM found at boot time.) I would imagine that get_vm_area()
  35. * would have to initialise this each time prior to calling vmregion_alloc().
  36. */
  37. struct arm_vmregion *
  38. arm_vmregion_alloc(struct arm_vmregion_head *head, size_t align,
  39. size_t size, gfp_t gfp, const void *caller)
  40. {
  41. unsigned long start = head->vm_start, addr = head->vm_end;
  42. unsigned long flags;
  43. struct arm_vmregion *c, *new;
  44. if (head->vm_end - head->vm_start < size) {
  45. printk(KERN_WARNING "%s: allocation too big (requested %#x, end:%lx, start:%lx)\n",
  46. __func__, size, head->vm_end, head->vm_start);
  47. goto out;
  48. }
  49. new = kmalloc(sizeof(struct arm_vmregion), gfp);
  50. if (!new)
  51. goto out;
  52. new->caller = caller;
  53. spin_lock_irqsave(&head->vm_lock, flags);
  54. addr = rounddown(addr - size, align);
  55. list_for_each_entry_reverse(c, &head->vm_list, vm_list) {
  56. if (addr >= c->vm_end)
  57. goto found;
  58. addr = rounddown(c->vm_start - size, align);
  59. if (addr < start)
  60. goto nospc;
  61. }
  62. found:
  63. /*
  64. * Insert this entry after the one we found.
  65. */
  66. list_add(&new->vm_list, &c->vm_list);
  67. new->vm_start = addr;
  68. new->vm_end = addr + size;
  69. new->vm_active = 1;
  70. spin_unlock_irqrestore(&head->vm_lock, flags);
  71. return new;
  72. nospc:
  73. spin_unlock_irqrestore(&head->vm_lock, flags);
  74. kfree(new);
  75. out:
  76. return NULL;
  77. }
  78. static struct arm_vmregion *__arm_vmregion_find(struct arm_vmregion_head *head, unsigned long addr)
  79. {
  80. struct arm_vmregion *c;
  81. list_for_each_entry(c, &head->vm_list, vm_list) {
  82. if (c->vm_active && c->vm_start == addr)
  83. goto out;
  84. }
  85. c = NULL;
  86. out:
  87. return c;
  88. }
  89. struct arm_vmregion *arm_vmregion_find(struct arm_vmregion_head *head, unsigned long addr)
  90. {
  91. struct arm_vmregion *c;
  92. unsigned long flags;
  93. spin_lock_irqsave(&head->vm_lock, flags);
  94. c = __arm_vmregion_find(head, addr);
  95. spin_unlock_irqrestore(&head->vm_lock, flags);
  96. return c;
  97. }
  98. struct arm_vmregion *arm_vmregion_find_remove(struct arm_vmregion_head *head, unsigned long addr)
  99. {
  100. struct arm_vmregion *c;
  101. unsigned long flags;
  102. spin_lock_irqsave(&head->vm_lock, flags);
  103. c = __arm_vmregion_find(head, addr);
  104. if (c)
  105. c->vm_active = 0;
  106. spin_unlock_irqrestore(&head->vm_lock, flags);
  107. return c;
  108. }
  109. void arm_vmregion_free(struct arm_vmregion_head *head, struct arm_vmregion *c)
  110. {
  111. unsigned long flags;
  112. spin_lock_irqsave(&head->vm_lock, flags);
  113. list_del(&c->vm_list);
  114. spin_unlock_irqrestore(&head->vm_lock, flags);
  115. kfree(c);
  116. }
  117. #ifdef CONFIG_PROC_FS
  118. static int arm_vmregion_show(struct seq_file *m, void *p)
  119. {
  120. struct arm_vmregion *c = list_entry(p, struct arm_vmregion, vm_list);
  121. seq_printf(m, "0x%08lx-0x%08lx %7lu", c->vm_start, c->vm_end,
  122. c->vm_end - c->vm_start);
  123. if (c->caller)
  124. seq_printf(m, " %pS", (void *)c->caller);
  125. seq_putc(m, '\n');
  126. return 0;
  127. }
  128. static void *arm_vmregion_start(struct seq_file *m, loff_t *pos)
  129. {
  130. struct arm_vmregion_head *h = m->private;
  131. spin_lock_irq(&h->vm_lock);
  132. return seq_list_start(&h->vm_list, *pos);
  133. }
  134. static void *arm_vmregion_next(struct seq_file *m, void *p, loff_t *pos)
  135. {
  136. struct arm_vmregion_head *h = m->private;
  137. return seq_list_next(p, &h->vm_list, pos);
  138. }
  139. static void arm_vmregion_stop(struct seq_file *m, void *p)
  140. {
  141. struct arm_vmregion_head *h = m->private;
  142. spin_unlock_irq(&h->vm_lock);
  143. }
  144. static const struct seq_operations arm_vmregion_ops = {
  145. .start = arm_vmregion_start,
  146. .stop = arm_vmregion_stop,
  147. .next = arm_vmregion_next,
  148. .show = arm_vmregion_show,
  149. };
  150. static int arm_vmregion_open(struct inode *inode, struct file *file)
  151. {
  152. struct arm_vmregion_head *h = PDE(inode)->data;
  153. int ret = seq_open(file, &arm_vmregion_ops);
  154. if (!ret) {
  155. struct seq_file *m = file->private_data;
  156. m->private = h;
  157. }
  158. return ret;
  159. }
  160. static const struct file_operations arm_vmregion_fops = {
  161. .open = arm_vmregion_open,
  162. .read = seq_read,
  163. .llseek = seq_lseek,
  164. .release = seq_release,
  165. };
  166. int arm_vmregion_create_proc(const char *path, struct arm_vmregion_head *h)
  167. {
  168. proc_create_data(path, S_IRUSR, NULL, &arm_vmregion_fops, h);
  169. return 0;
  170. }
  171. #else
  172. int arm_vmregion_create_proc(const char *path, struct arm_vmregion_head *h)
  173. {
  174. return 0;
  175. }
  176. #endif