vmregion.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #include <linux/spinlock.h>
  2. #include <linux/list.h>
  3. #include <linux/slab.h>
  4. #include "vmregion.h"
  5. /*
  6. * VM region handling support.
  7. *
  8. * This should become something generic, handling VM region allocations for
  9. * vmalloc and similar (ioremap, module space, etc).
  10. *
  11. * I envisage vmalloc()'s supporting vm_struct becoming:
  12. *
  13. * struct vm_struct {
  14. * struct vmregion region;
  15. * unsigned long flags;
  16. * struct page **pages;
  17. * unsigned int nr_pages;
  18. * unsigned long phys_addr;
  19. * };
  20. *
  21. * get_vm_area() would then call vmregion_alloc with an appropriate
  22. * struct vmregion head (eg):
  23. *
  24. * struct vmregion vmalloc_head = {
  25. * .vm_list = LIST_HEAD_INIT(vmalloc_head.vm_list),
  26. * .vm_start = VMALLOC_START,
  27. * .vm_end = VMALLOC_END,
  28. * };
  29. *
  30. * However, vmalloc_head.vm_start is variable (typically, it is dependent on
  31. * the amount of RAM found at boot time.) I would imagine that get_vm_area()
  32. * would have to initialise this each time prior to calling vmregion_alloc().
  33. */
  34. struct arm_vmregion *
  35. arm_vmregion_alloc(struct arm_vmregion_head *head, size_t align,
  36. size_t size, gfp_t gfp)
  37. {
  38. unsigned long start = head->vm_start, addr = head->vm_end;
  39. unsigned long flags;
  40. struct arm_vmregion *c, *new;
  41. if (head->vm_end - head->vm_start < size) {
  42. printk(KERN_WARNING "%s: allocation too big (requested %#x)\n",
  43. __func__, size);
  44. goto out;
  45. }
  46. new = kmalloc(sizeof(struct arm_vmregion), gfp);
  47. if (!new)
  48. goto out;
  49. spin_lock_irqsave(&head->vm_lock, flags);
  50. addr = rounddown(addr - size, align);
  51. list_for_each_entry_reverse(c, &head->vm_list, vm_list) {
  52. if (addr >= c->vm_end)
  53. goto found;
  54. addr = rounddown(c->vm_start - size, align);
  55. if (addr < start)
  56. goto nospc;
  57. }
  58. found:
  59. /*
  60. * Insert this entry after the one we found.
  61. */
  62. list_add(&new->vm_list, &c->vm_list);
  63. new->vm_start = addr;
  64. new->vm_end = addr + size;
  65. new->vm_active = 1;
  66. spin_unlock_irqrestore(&head->vm_lock, flags);
  67. return new;
  68. nospc:
  69. spin_unlock_irqrestore(&head->vm_lock, flags);
  70. kfree(new);
  71. out:
  72. return NULL;
  73. }
  74. static struct arm_vmregion *__arm_vmregion_find(struct arm_vmregion_head *head, unsigned long addr)
  75. {
  76. struct arm_vmregion *c;
  77. list_for_each_entry(c, &head->vm_list, vm_list) {
  78. if (c->vm_active && c->vm_start == addr)
  79. goto out;
  80. }
  81. c = NULL;
  82. out:
  83. return c;
  84. }
  85. struct arm_vmregion *arm_vmregion_find(struct arm_vmregion_head *head, unsigned long addr)
  86. {
  87. struct arm_vmregion *c;
  88. unsigned long flags;
  89. spin_lock_irqsave(&head->vm_lock, flags);
  90. c = __arm_vmregion_find(head, addr);
  91. spin_unlock_irqrestore(&head->vm_lock, flags);
  92. return c;
  93. }
  94. struct arm_vmregion *arm_vmregion_find_remove(struct arm_vmregion_head *head, unsigned long addr)
  95. {
  96. struct arm_vmregion *c;
  97. unsigned long flags;
  98. spin_lock_irqsave(&head->vm_lock, flags);
  99. c = __arm_vmregion_find(head, addr);
  100. if (c)
  101. c->vm_active = 0;
  102. spin_unlock_irqrestore(&head->vm_lock, flags);
  103. return c;
  104. }
  105. void arm_vmregion_free(struct arm_vmregion_head *head, struct arm_vmregion *c)
  106. {
  107. unsigned long flags;
  108. spin_lock_irqsave(&head->vm_lock, flags);
  109. list_del(&c->vm_list);
  110. spin_unlock_irqrestore(&head->vm_lock, flags);
  111. kfree(c);
  112. }