Idenfity devices by name, not by char [1/3]
[akaros.git] / kern / src / dmapool.c
1 /*
2  * DMA Pool allocator
3  *
4  * Copyright 2001 David Brownell
5  * Copyright 2007 Intel Corporation
6  *   Author: Matthew Wilcox <willy@linux.intel.com>
7  *
8  * This software may be redistributed and/or modified under the terms of
9  * the GNU General Public License ("GPL") version 2 as published by the
10  * Free Software Foundation.
11  *
12  * This allocator returns small blocks of a given size which are DMA-able by
13  * the given device.  It uses the dma_alloc_coherent page allocator to get
14  * new pages, then splits them up into blocks of the required size.
15  * Many older drivers still have their own code to do this.
16  *
17  * The current design of this allocator is fairly simple.  The pool is
18  * represented by the 'struct dma_pool' which keeps a doubly-linked list of
19  * allocated pages.  Each page in the page_list is split into blocks of at
20  * least 'size' bytes.  Free blocks are tracked in an unsorted singly-linked
21  * list of free blocks within the page.  Used blocks aren't tracked, but we
22  * keep a count of how many are currently allocated from each page.
23  */
24
25 #include <linux_compat.h>
26
27 struct dma_pool {
28         struct list_head page_list;
29         spinlock_t lock;
30         size_t size;
31         void *dev;
32         size_t allocation;
33         size_t boundary;
34         char name[32];
35         struct list_head pools;
36 };
37
38 struct dma_page {
39         struct list_head page_list;
40         void *vaddr;
41         dma_addr_t dma;
42         unsigned int in_use;
43         unsigned int offset;
44 };
45
46 /**
47  * dma_pool_create - Creates a pool of consistent memory blocks, for dma.
48  */
49 struct dma_pool *dma_pool_create(const char *name, void *dev,
50                                  size_t size, size_t align, size_t boundary)
51 {
52         struct dma_pool *retval;
53         size_t allocation;
54
55         if (align == 0)
56                 align = 1;
57         else if (align & (align - 1))
58                 return NULL;
59
60         if (size == 0)
61                 return NULL;
62         else if (size < 4)
63                 size = 4;
64
65         if ((size % align) != 0)
66                 size = ALIGN(size, align);
67
68         allocation = MAX_T(size_t, size, PAGE_SIZE);
69
70         if (!boundary)
71                 boundary = allocation;
72         else if ((boundary < size) || (boundary & (boundary - 1)))
73                 return NULL;
74
75         retval = kmalloc(sizeof(*retval), KMALLOC_WAIT);
76         if (!retval)
77                 return retval;
78
79         strlcpy(retval->name, name, sizeof(retval->name));
80
81         retval->dev = dev;      /* FIXME */
82
83         INIT_LIST_HEAD(&retval->page_list);
84         spinlock_init(&retval->lock);
85         retval->size = size;
86         retval->boundary = boundary;
87         retval->allocation = allocation;
88
89         INIT_LIST_HEAD(&retval->pools);
90
91         /* TODO device_create_file */
92
93         return retval;
94 }
95
96 void dma_pool_destroy(struct dma_pool *pool)
97 {
98         /* TODO */
99 }
100
101 static void pool_initialise_page(struct dma_pool *pool, struct dma_page *page)
102 {
103         unsigned int offset = 0;
104         unsigned int next_boundary = pool->boundary;
105
106         do {
107                 unsigned int next = offset + pool->size;
108                 if (unlikely((next + pool->size) >= next_boundary)) {
109                         next = next_boundary;
110                         next_boundary += pool->boundary;
111                 }
112                 *(int *)(page->vaddr + offset) = next;
113                 offset = next;
114         } while (offset < pool->allocation);
115 }
116
117 static struct dma_page *pool_alloc_page(struct dma_pool *pool, int mem_flags)
118 {
119         struct dma_page *page;
120
121         page = kmalloc(sizeof(*page), mem_flags);
122         if (!page)
123                 return NULL;
124         page->vaddr = dma_alloc_coherent(pool->dev, pool->allocation,
125                                          &page->dma, mem_flags);
126         if (page->vaddr) {
127                 pool_initialise_page(pool, page);
128                 page->in_use = 0;
129                 page->offset = 0;
130         } else {
131                 kfree(page);
132                 page = NULL;
133         }
134         return page;
135 }
136
137 void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle)
138 {
139         struct dma_page *page;
140         size_t offset;
141         void *retval;
142
143         /* FIXME take care of locks */
144
145         list_for_each_entry(page, &pool->page_list, page_list) {
146                 if (page->offset < pool->allocation)
147                         goto ready;
148         }
149
150         page = pool_alloc_page(pool, mem_flags);
151         if (!page)
152                 return NULL;
153
154         list_add(&page->page_list, &pool->page_list);
155 ready:
156         page->in_use++;
157         offset = page->offset;
158         page->offset = *(int *)(page->vaddr + offset);  /* "next" */
159         retval = offset + page->vaddr;
160         *handle = offset + page->dma;
161         return retval;
162 }
163
164 void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr)
165 {
166         /* TODO */
167 }