bzr branch
http://suren.me/webbzr/alps/pcitool
1
by Suren A. Chilingaryan
Initial import |
1 |
/**
|
2 |
*
|
|
3 |
* @file kmem.c
|
|
4 |
* @brief This file contains all functions dealing with kernel memory.
|
|
5 |
* @author Guillermo Marcus
|
|
6 |
* @date 2009-04-05
|
|
7 |
*
|
|
8 |
*/
|
|
9 |
#include <linux/version.h> |
|
10 |
#include <linux/string.h> |
|
11 |
#include <linux/types.h> |
|
12 |
#include <linux/list.h> |
|
13 |
#include <linux/interrupt.h> |
|
14 |
#include <linux/pci.h> |
|
15 |
#include <linux/cdev.h> |
|
16 |
#include <linux/wait.h> |
|
17 |
#include <linux/mm.h> |
|
18 |
#include <linux/pagemap.h> |
|
19 |
||
365
by Suren A. Chilingaryan
Restructure driver headers |
20 |
#include "base.h" |
1
by Suren A. Chilingaryan
Initial import |
21 |
|
205
by Suren A. Chilingaryan
Handle 3.7+ kernels which are missing VM_RESERVED flag |
22 |
|
1
by Suren A. Chilingaryan
Initial import |
23 |
/**
|
24 |
*
|
|
25 |
* Allocates new kernel memory including the corresponding management structure, makes
|
|
26 |
* it available via sysfs if possible.
|
|
27 |
*
|
|
28 |
*/
|
|
29 |
int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle) |
|
30 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
31 |
int flags; |
32 |
pcidriver_kmem_entry_t *kmem_entry; |
|
33 |
void *retptr; |
|
34 |
||
35 |
if (kmem_handle->flags&KMEM_FLAG_REUSE) { |
|
36 |
kmem_entry = pcidriver_kmem_find_entry_use(privdata, kmem_handle->use, kmem_handle->item); |
|
37 |
if (kmem_entry) { |
|
38 |
unsigned long flags = kmem_handle->flags; |
|
39 |
||
40 |
if (flags&KMEM_FLAG_TRY) { |
|
41 |
kmem_handle->type = kmem_entry->type; |
|
42 |
kmem_handle->size = kmem_entry->size; |
|
43 |
kmem_handle->align = kmem_entry->align; |
|
44 |
} else { |
|
45 |
if (kmem_handle->type != kmem_entry->type) { |
|
46 |
mod_info("Invalid type of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->type, kmem_handle->type); |
|
47 |
kmem_handle->type = kmem_entry->type; |
|
48 |
return -EINVAL; |
|
49 |
}
|
|
50 |
||
51 |
if (((kmem_handle->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_PAGE)&&(kmem_handle->size == 0)) { |
|
52 |
kmem_handle->size = kmem_entry->size; |
|
53 |
} else if (kmem_handle->size != kmem_entry->size) { |
|
54 |
mod_info("Invalid size of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->size, kmem_handle->size); |
|
55 |
kmem_handle->size = kmem_entry->size; |
|
56 |
return -EINVAL; |
|
57 |
}
|
|
58 |
||
59 |
if (kmem_handle->align != kmem_entry->align) { |
|
60 |
mod_info("Invalid alignment of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->align, kmem_handle->align); |
|
61 |
kmem_handle->align = kmem_entry->align; |
|
62 |
return -EINVAL; |
|
63 |
}
|
|
64 |
||
65 |
if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?1:0) != ((flags&KMEM_FLAG_EXCLUSIVE)?1:0)) { |
|
66 |
mod_info("Invalid mode of reusable kmem_entry\n"); |
|
67 |
kmem_handle->flags = (kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?KMEM_FLAG_EXCLUSIVE:0; |
|
68 |
return -EINVAL; |
|
69 |
}
|
|
70 |
}
|
|
71 |
||
72 |
||
73 |
if ((kmem_entry->mode&KMEM_MODE_COUNT)==KMEM_MODE_COUNT) { |
|
74 |
mod_info("Reuse counter of kmem_entry is overflown"); |
|
75 |
return -EBUSY; |
|
76 |
}
|
|
77 |
||
78 |
||
79 |
kmem_handle->handle_id = kmem_entry->id; |
|
80 |
kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle); |
|
81 |
kmem_handle->pa = virt_to_phys((void*)kmem_entry->cpua); |
|
82 |
||
83 |
kmem_handle->flags = KMEM_FLAG_REUSED; |
|
84 |
if (kmem_entry->refs&KMEM_REF_HW) kmem_handle->flags |= KMEM_FLAG_REUSED_HW; |
|
85 |
if (kmem_entry->mode&KMEM_MODE_PERSISTENT) kmem_handle->flags |= KMEM_FLAG_REUSED_PERSISTENT; |
|
86 |
||
87 |
kmem_entry->mode += 1; |
|
88 |
if (flags&KMEM_FLAG_HW) { |
|
89 |
if ((kmem_entry->refs&KMEM_REF_HW)==0) |
|
90 |
pcidriver_module_get(privdata); |
|
91 |
||
92 |
kmem_entry->refs |= KMEM_REF_HW; |
|
93 |
}
|
|
94 |
if (flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT; |
|
95 |
||
96 |
privdata->kmem_cur_id = kmem_entry->id; |
|
97 |
||
98 |
return 0; |
|
99 |
}
|
|
100 |
||
101 |
if (kmem_handle->flags&KMEM_FLAG_TRY) return -ENOENT; |
|
102 |
}
|
|
103 |
||
104 |
/* First, allocate zeroed memory for the kmem_entry */
|
|
105 |
if ((kmem_entry = kcalloc(1, sizeof(pcidriver_kmem_entry_t), GFP_KERNEL)) == NULL) |
|
106 |
goto kmem_alloc_entry_fail; |
|
107 |
||
108 |
/* Initialize the kmem_entry */
|
|
109 |
kmem_entry->id = atomic_inc_return(&privdata->kmem_count) - 1; |
|
110 |
privdata->kmem_cur_id = kmem_entry->id; |
|
111 |
kmem_handle->handle_id = kmem_entry->id; |
|
112 |
||
113 |
kmem_entry->use = kmem_handle->use; |
|
114 |
kmem_entry->item = kmem_handle->item; |
|
115 |
kmem_entry->type = kmem_handle->type; |
|
116 |
kmem_entry->align = kmem_handle->align; |
|
117 |
kmem_entry->direction = PCI_DMA_NONE; |
|
118 |
||
119 |
/* Initialize sysfs if possible */
|
|
120 |
if (pcidriver_sysfs_initialize_kmem(privdata, kmem_entry->id, &(kmem_entry->sysfs_attr)) != 0) |
|
121 |
goto kmem_alloc_mem_fail; |
|
122 |
||
123 |
/* ...and allocate the DMA memory */
|
|
124 |
/* note this is a memory pair, referencing the same area: the cpu address (cpua)
|
|
125 |
* and the PCI bus address (pa). The CPU and PCI addresses may not be the same.
|
|
126 |
* The CPU sees only CPU addresses, while the device sees only PCI addresses.
|
|
127 |
* CPU address is used for the mmap (internal to the driver), and
|
|
128 |
* PCI address is the address passed to the DMA Controller in the device.
|
|
129 |
*/
|
|
130 |
switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) { |
|
131 |
case PCILIB_KMEM_TYPE_CONSISTENT: |
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
132 |
#ifdef PCIDRIVER_DUMMY_DEVICE
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
133 |
retptr = kmalloc( kmem_handle->size, GFP_KERNEL); |
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
134 |
#else /* PCIDRIVER_DUMMY_DEVICE */ |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
135 |
retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) ); |
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
136 |
#endif /* PCIDRIVER_DUMMY_DEVICE */ |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
137 |
break; |
138 |
case PCILIB_KMEM_TYPE_REGION: |
|
139 |
retptr = ioremap(kmem_handle->pa, kmem_handle->size); |
|
140 |
kmem_entry->dma_handle = kmem_handle->pa; |
|
141 |
if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_S2C) { |
|
142 |
kmem_entry->direction = PCI_DMA_TODEVICE; |
|
143 |
} else if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_C2S) { |
|
144 |
kmem_entry->direction = PCI_DMA_FROMDEVICE; |
|
145 |
}
|
|
146 |
break; |
|
147 |
case PCILIB_KMEM_TYPE_PAGE: |
|
148 |
flags = GFP_KERNEL; |
|
149 |
||
150 |
if (kmem_handle->size == 0) |
|
151 |
kmem_handle->size = PAGE_SIZE; |
|
152 |
else if (kmem_handle->size%PAGE_SIZE) |
|
153 |
goto kmem_alloc_mem_fail; |
|
154 |
else
|
|
155 |
flags |= __GFP_COMP; |
|
156 |
||
157 |
retptr = (void*)__get_free_pages(flags, get_order(kmem_handle->size)); |
|
158 |
kmem_entry->dma_handle = 0; |
|
159 |
||
160 |
if (retptr) { |
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
161 |
#ifndef PCIDRIVER_DUMMY_DEVICE
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
162 |
if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) { |
163 |
kmem_entry->direction = PCI_DMA_TODEVICE; |
|
164 |
kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_TODEVICE); |
|
165 |
if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) { |
|
166 |
free_pages((unsigned long)retptr, get_order(kmem_handle->size)); |
|
167 |
goto kmem_alloc_mem_fail; |
|
168 |
}
|
|
169 |
} else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) { |
|
170 |
kmem_entry->direction = PCI_DMA_FROMDEVICE; |
|
171 |
kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_FROMDEVICE); |
|
172 |
if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) { |
|
173 |
free_pages((unsigned long)retptr, get_order(kmem_handle->size)); |
|
174 |
goto kmem_alloc_mem_fail; |
|
175 |
||
176 |
}
|
|
177 |
}
|
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
178 |
#endif /* ! PCIDRIVER_DUMMY_DEVICE */ |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
179 |
}
|
180 |
||
181 |
break; |
|
182 |
default: |
|
183 |
goto kmem_alloc_mem_fail; |
|
184 |
}
|
|
185 |
||
186 |
||
187 |
if (retptr == NULL) |
|
188 |
goto kmem_alloc_mem_fail; |
|
189 |
||
190 |
kmem_entry->size = kmem_handle->size; |
|
191 |
kmem_entry->cpua = (unsigned long)retptr; |
|
192 |
kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle); |
|
193 |
kmem_handle->pa = virt_to_phys(retptr); |
|
194 |
||
195 |
kmem_entry->mode = 1; |
|
196 |
if (kmem_handle->flags&KMEM_FLAG_REUSE) { |
|
197 |
kmem_entry->mode |= KMEM_MODE_REUSABLE; |
|
198 |
if (kmem_handle->flags&KMEM_FLAG_EXCLUSIVE) kmem_entry->mode |= KMEM_MODE_EXCLUSIVE; |
|
199 |
if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT; |
|
200 |
}
|
|
201 |
||
202 |
kmem_entry->refs = 0; |
|
203 |
if (kmem_handle->flags&KMEM_FLAG_HW) { |
|
204 |
pcidriver_module_get(privdata); |
|
205 |
||
206 |
kmem_entry->refs |= KMEM_REF_HW; |
|
207 |
}
|
|
208 |
||
209 |
kmem_handle->flags = 0; |
|
210 |
||
211 |
/* Add the kmem_entry to the list of the device */
|
|
212 |
spin_lock( &(privdata->kmemlist_lock) ); |
|
213 |
list_add_tail( &(kmem_entry->list), &(privdata->kmem_list) ); |
|
214 |
spin_unlock( &(privdata->kmemlist_lock) ); |
|
215 |
||
216 |
return 0; |
|
1
by Suren A. Chilingaryan
Initial import |
217 |
|
218 |
kmem_alloc_mem_fail: |
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
219 |
kfree(kmem_entry); |
1
by Suren A. Chilingaryan
Initial import |
220 |
kmem_alloc_entry_fail: |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
221 |
return -ENOMEM; |
1
by Suren A. Chilingaryan
Initial import |
222 |
}
|
223 |
||
81
by Suren A. Chilingaryan
Support forceful clean-up of kernel memory |
224 |
static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle, pcidriver_kmem_entry_t *kmem_entry) { |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
225 |
if ((kmem_handle->flags & KMEM_FLAG_FORCE) == 0) { |
226 |
if (kmem_entry->mode&KMEM_MODE_COUNT) |
|
227 |
kmem_entry->mode -= 1; |
|
228 |
||
229 |
if (kmem_handle->flags&KMEM_FLAG_HW) { |
|
230 |
if (kmem_entry->refs&KMEM_REF_HW) |
|
231 |
pcidriver_module_put(privdata); |
|
232 |
||
233 |
kmem_entry->refs &= ~KMEM_REF_HW; |
|
234 |
}
|
|
235 |
||
236 |
if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) |
|
237 |
kmem_entry->mode &= ~KMEM_MODE_PERSISTENT; |
|
238 |
||
239 |
if (kmem_handle->flags&KMEM_FLAG_REUSE) |
|
240 |
return 0; |
|
241 |
||
366
by Suren A. Chilingaryan
Minor change of logic in pcidriver_kmem_free_check() resulting in less complains while de-referencing the shared non-persistent kernel memory |
242 |
if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0)) |
243 |
return 0; |
|
244 |
||
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
245 |
if (kmem_entry->refs) { |
246 |
kmem_entry->mode += 1; |
|
247 |
mod_info("can't free referenced kmem_entry, refs = %lx\n", kmem_entry->refs); |
|
248 |
return -EBUSY; |
|
249 |
}
|
|
250 |
||
251 |
if (kmem_entry->mode & KMEM_MODE_PERSISTENT) { |
|
252 |
kmem_entry->mode += 1; |
|
253 |
mod_info("can't free persistent kmem_entry\n"); |
|
254 |
return -EBUSY; |
|
255 |
}
|
|
256 |
||
257 |
} else { |
|
258 |
if (kmem_entry->refs&KMEM_REF_HW) |
|
259 |
pcidriver_module_put(privdata); |
|
260 |
||
261 |
while (!atomic_add_negative(-1, &(privdata->refs))) pcidriver_module_put(privdata); |
|
262 |
atomic_inc(&(privdata->refs)); |
|
263 |
||
264 |
}
|
|
265 |
||
266 |
return 1; |
|
81
by Suren A. Chilingaryan
Support forceful clean-up of kernel memory |
267 |
}
|
268 |
||
269 |
static int pcidriver_kmem_free_use(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle) |
|
270 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
271 |
int err; |
272 |
int failed = 0; |
|
273 |
struct list_head *ptr, *next; |
|
274 |
pcidriver_kmem_entry_t *kmem_entry; |
|
275 |
||
276 |
/* iterate safely over the entries and delete them */
|
|
277 |
list_for_each_safe(ptr, next, &(privdata->kmem_list)) { |
|
278 |
kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list); |
|
279 |
if (kmem_entry->use == kmem_handle->use) { |
|
280 |
err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry); |
|
281 |
if (err > 0) |
|
282 |
pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */ |
|
283 |
else
|
|
284 |
failed = 1; |
|
285 |
}
|
|
286 |
}
|
|
287 |
||
288 |
if (failed) { |
|
289 |
mod_info("Some kmem_entries for use %lx are still referenced\n", kmem_handle->use); |
|
290 |
return -EBUSY; |
|
291 |
}
|
|
292 |
||
293 |
return 0; |
|
81
by Suren A. Chilingaryan
Support forceful clean-up of kernel memory |
294 |
}
|
295 |
||
296 |
/**
|
|
297 |
*
|
|
298 |
* Called via sysfs, frees kernel memory and the corresponding management structure
|
|
299 |
*
|
|
300 |
*/
|
|
301 |
int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle ) |
|
302 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
303 |
int err; |
304 |
pcidriver_kmem_entry_t *kmem_entry; |
|
305 |
||
306 |
if (kmem_handle->flags&KMEM_FLAG_MASS) { |
|
307 |
kmem_handle->flags &= ~KMEM_FLAG_MASS; |
|
308 |
return pcidriver_kmem_free_use(privdata, kmem_handle); |
|
309 |
}
|
|
310 |
||
311 |
/* Find the associated kmem_entry for this buffer */
|
|
312 |
if ((kmem_entry = pcidriver_kmem_find_entry(privdata, kmem_handle)) == NULL) |
|
313 |
return -EINVAL; /* kmem_handle is not valid */ |
|
314 |
||
315 |
err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry); |
|
316 |
||
317 |
if (err > 0) |
|
318 |
return pcidriver_kmem_free_entry(privdata, kmem_entry); |
|
319 |
||
320 |
return err; |
|
1
by Suren A. Chilingaryan
Initial import |
321 |
}
|
322 |
||
323 |
/**
|
|
324 |
*
|
|
325 |
* Called when cleaning up, frees all kernel memory and their corresponding management structure
|
|
326 |
*
|
|
327 |
*/
|
|
328 |
int pcidriver_kmem_free_all(pcidriver_privdata_t *privdata) |
|
329 |
{
|
|
85
by Suren A. Chilingaryan
Prevent driver holding hardware locks from unloading |
330 |
// int failed = 0;
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
331 |
struct list_head *ptr, *next; |
332 |
pcidriver_kmem_entry_t *kmem_entry; |
|
1
by Suren A. Chilingaryan
Initial import |
333 |
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
334 |
/* iterate safely over the entries and delete them */
|
335 |
list_for_each_safe(ptr, next, &(privdata->kmem_list)) { |
|
336 |
kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list); |
|
337 |
/*if (kmem_entry->refs)
|
|
338 |
failed = 1;
|
|
339 |
else*/
|
|
340 |
pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */ |
|
341 |
}
|
|
342 |
/*
|
|
343 |
if (failed) {
|
|
344 |
mod_info("Some kmem_entries are still referenced\n");
|
|
345 |
return -EBUSY;
|
|
346 |
}
|
|
347 |
*/
|
|
348 |
return 0; |
|
1
by Suren A. Chilingaryan
Initial import |
349 |
}
|
350 |
||
148
by Suren A. Chilingaryan
Synchronize kernel buffers during the read-kernel-memory and while accessing via sysfs |
351 |
|
1
by Suren A. Chilingaryan
Initial import |
352 |
/**
|
353 |
*
|
|
354 |
* Synchronize memory to/from the device (or in both directions).
|
|
355 |
*
|
|
356 |
*/
|
|
148
by Suren A. Chilingaryan
Synchronize kernel buffers during the read-kernel-memory and while accessing via sysfs |
357 |
int pcidriver_kmem_sync_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry, int direction) |
1
by Suren A. Chilingaryan
Initial import |
358 |
{
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
359 |
if (kmem_entry->direction == PCI_DMA_NONE) |
360 |
return -EINVAL; |
|
105
by Suren A. Chilingaryan
Properly perform synchronization of DMA buffers |
361 |
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
362 |
#ifndef PCIDRIVER_DUMMY_DEVICE
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
363 |
switch (direction) { |
364 |
case PCILIB_KMEM_SYNC_TODEVICE: |
|
365 |
pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); |
|
366 |
break; |
|
367 |
case PCILIB_KMEM_SYNC_FROMDEVICE: |
|
368 |
pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); |
|
369 |
break; |
|
370 |
case PCILIB_KMEM_SYNC_BIDIRECTIONAL: |
|
371 |
pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); |
|
372 |
pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); |
|
373 |
break; |
|
374 |
default: |
|
375 |
return -EINVAL; /* wrong direction parameter */ |
|
376 |
}
|
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
377 |
#endif /* ! PCIDRIVER_DUMMY_DEVICE */ |
1
by Suren A. Chilingaryan
Initial import |
378 |
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
379 |
return 0; /* success */ |
1
by Suren A. Chilingaryan
Initial import |
380 |
}
|
381 |
||
382 |
/**
|
|
383 |
*
|
|
148
by Suren A. Chilingaryan
Synchronize kernel buffers during the read-kernel-memory and while accessing via sysfs |
384 |
* Synchronize memory to/from the device (or in both directions).
|
385 |
*
|
|
386 |
*/
|
|
387 |
int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync ) |
|
388 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
389 |
pcidriver_kmem_entry_t *kmem_entry = NULL; |
390 |
||
391 |
/*
|
|
392 |
* This is a shortcut to quickly find a next item in big multi-page kernel buffers
|
|
393 |
*/
|
|
394 |
spin_lock(&(privdata->kmemlist_lock)); |
|
395 |
if (privdata->kmem_last_sync) { |
|
396 |
if (privdata->kmem_last_sync->id == kmem_sync->handle.handle_id) |
|
397 |
kmem_entry = privdata->kmem_last_sync; |
|
398 |
else { |
|
399 |
privdata->kmem_last_sync = container_of(privdata->kmem_last_sync->list.next, pcidriver_kmem_entry_t, list); |
|
400 |
||
401 |
if (privdata->kmem_last_sync) { |
|
402 |
if (privdata->kmem_last_sync->id == kmem_sync->handle.handle_id) |
|
403 |
kmem_entry = privdata->kmem_last_sync; |
|
404 |
else
|
|
405 |
privdata->kmem_last_sync = NULL; |
|
406 |
}
|
|
407 |
}
|
|
408 |
}
|
|
409 |
spin_unlock(&(privdata->kmemlist_lock)); |
|
410 |
||
411 |
/*
|
|
412 |
* If not found go the standard way
|
|
413 |
*/
|
|
414 |
if (!kmem_entry) { |
|
415 |
if ((kmem_entry = pcidriver_kmem_find_entry(privdata, &(kmem_sync->handle))) == NULL) |
|
416 |
return -EINVAL; /* kmem_handle is not valid */ |
|
417 |
||
418 |
spin_lock(&(privdata->kmemlist_lock)); |
|
419 |
privdata->kmem_last_sync = kmem_entry; |
|
420 |
spin_unlock(&(privdata->kmemlist_lock)); |
|
421 |
}
|
|
422 |
||
423 |
return pcidriver_kmem_sync_entry(privdata, kmem_entry, kmem_sync->dir); |
|
148
by Suren A. Chilingaryan
Synchronize kernel buffers during the read-kernel-memory and while accessing via sysfs |
424 |
}
|
425 |
||
426 |
/**
|
|
427 |
*
|
|
1
by Suren A. Chilingaryan
Initial import |
428 |
* Free the given kmem_entry and its memory.
|
429 |
*
|
|
430 |
*/
|
|
431 |
int pcidriver_kmem_free_entry(pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry) |
|
432 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
433 |
pcidriver_sysfs_remove(privdata, &(kmem_entry->sysfs_attr)); |
434 |
||
435 |
/* Release DMA memory */
|
|
436 |
switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) { |
|
437 |
case PCILIB_KMEM_TYPE_CONSISTENT: |
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
438 |
#ifdef PCIDRIVER_DUMMY_DEVICE
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
439 |
kfree((void*)(kmem_entry->cpua)); |
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
440 |
#else /* PCIDRIVER_DUMMY_DEVICE */ |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
441 |
pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle ); |
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
442 |
#endif /* PCIDRIVER_DUMMY_DEVICE */ |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
443 |
break; |
444 |
case PCILIB_KMEM_TYPE_REGION: |
|
445 |
iounmap((void *)(kmem_entry->cpua)); |
|
446 |
break; |
|
447 |
case PCILIB_KMEM_TYPE_PAGE: |
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
448 |
#ifndef PCIDRIVER_DUMMY_DEVICE
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
449 |
if (kmem_entry->dma_handle) { |
450 |
if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) { |
|
451 |
pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE); |
|
452 |
} else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) { |
|
453 |
pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_FROMDEVICE); |
|
454 |
}
|
|
455 |
}
|
|
358
by Suren A. Chilingaryan
Support emulation mode without real hardware |
456 |
#endif /* ! PCIDRIVER_DUMMY_DEVICE */ |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
457 |
free_pages((unsigned long)kmem_entry->cpua, get_order(kmem_entry->size)); |
458 |
break; |
|
459 |
}
|
|
460 |
||
461 |
||
462 |
/* Remove the kmem list entry */
|
|
463 |
spin_lock( &(privdata->kmemlist_lock) ); |
|
464 |
if (privdata->kmem_last_sync == kmem_entry) |
|
465 |
privdata->kmem_last_sync = NULL; |
|
466 |
list_del( &(kmem_entry->list) ); |
|
467 |
spin_unlock( &(privdata->kmemlist_lock) ); |
|
468 |
||
469 |
/* Release kmem_entry memory */
|
|
470 |
kfree(kmem_entry); |
|
471 |
||
472 |
return 0; |
|
1
by Suren A. Chilingaryan
Initial import |
473 |
}
|
474 |
||
475 |
/**
|
|
476 |
*
|
|
477 |
* Find the corresponding kmem_entry for the given kmem_handle.
|
|
478 |
*
|
|
479 |
*/
|
|
480 |
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle) |
|
481 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
482 |
struct list_head *ptr; |
483 |
pcidriver_kmem_entry_t *entry, *result = NULL; |
|
484 |
||
485 |
/* should I implement it better using the handle_id? */
|
|
486 |
||
487 |
spin_lock(&(privdata->kmemlist_lock)); |
|
488 |
list_for_each(ptr, &(privdata->kmem_list)) { |
|
489 |
entry = list_entry(ptr, pcidriver_kmem_entry_t, list); |
|
490 |
||
491 |
if (entry->id == kmem_handle->handle_id) { |
|
492 |
result = entry; |
|
493 |
break; |
|
494 |
}
|
|
495 |
}
|
|
496 |
||
497 |
spin_unlock(&(privdata->kmemlist_lock)); |
|
498 |
return result; |
|
1
by Suren A. Chilingaryan
Initial import |
499 |
}
|
500 |
||
501 |
/**
|
|
502 |
*
|
|
503 |
* find the corresponding kmem_entry for the given id.
|
|
504 |
*
|
|
505 |
*/
|
|
506 |
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id(pcidriver_privdata_t *privdata, int id) |
|
507 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
508 |
struct list_head *ptr; |
509 |
pcidriver_kmem_entry_t *entry, *result = NULL; |
|
510 |
||
511 |
spin_lock(&(privdata->kmemlist_lock)); |
|
512 |
list_for_each(ptr, &(privdata->kmem_list)) { |
|
513 |
entry = list_entry(ptr, pcidriver_kmem_entry_t, list); |
|
514 |
||
515 |
if (entry->id == id) { |
|
516 |
result = entry; |
|
517 |
break; |
|
518 |
}
|
|
519 |
}
|
|
520 |
||
521 |
spin_unlock(&(privdata->kmemlist_lock)); |
|
522 |
return result; |
|
1
by Suren A. Chilingaryan
Initial import |
523 |
}
|
524 |
||
525 |
/**
|
|
526 |
*
|
|
71
by Suren A. Chilingaryan
First iteration of work to preserve DMA state between executions |
527 |
* find the corresponding kmem_entry for the given use and item.
|
528 |
*
|
|
529 |
*/
|
|
530 |
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_use(pcidriver_privdata_t *privdata, unsigned long use, unsigned long item) |
|
531 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
532 |
struct list_head *ptr; |
533 |
pcidriver_kmem_entry_t *entry, *result = NULL; |
|
534 |
||
535 |
spin_lock(&(privdata->kmemlist_lock)); |
|
536 |
list_for_each(ptr, &(privdata->kmem_list)) { |
|
537 |
entry = list_entry(ptr, pcidriver_kmem_entry_t, list); |
|
538 |
||
539 |
if ((entry->use == use)&&(entry->item == item)&&(entry->mode&KMEM_MODE_REUSABLE)) { |
|
540 |
result = entry; |
|
541 |
break; |
|
542 |
}
|
|
543 |
}
|
|
544 |
||
545 |
spin_unlock(&(privdata->kmemlist_lock)); |
|
546 |
return result; |
|
71
by Suren A. Chilingaryan
First iteration of work to preserve DMA state between executions |
547 |
}
|
548 |
||
549 |
||
73
by Suren A. Chilingaryan
Implement DMA access synchronization in the driver |
550 |
void pcidriver_kmem_mmap_close(struct vm_area_struct *vma) { |
76
by Suren A. Chilingaryan
Handle correctly reference counting in the driver |
551 |
unsigned long vma_size; |
73
by Suren A. Chilingaryan
Implement DMA access synchronization in the driver |
552 |
pcidriver_kmem_entry_t *kmem_entry = (pcidriver_kmem_entry_t*)vma->vm_private_data; |
76
by Suren A. Chilingaryan
Handle correctly reference counting in the driver |
553 |
if (kmem_entry) { |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
554 |
/*
|
555 |
if (kmem_entry->id == 0) {
|
|
556 |
mod_info("refs: %p %p %lx\n", vma, vma->vm_private_data, kmem_entry->refs);
|
|
557 |
mod_info("kmem_size: %lu vma_size: %lu, s: %lx, e: %lx\n", kmem_entry->size, (vma->vm_end - vma->vm_start), vma->vm_start, vma->vm_end);
|
|
558 |
}
|
|
559 |
*/
|
|
560 |
||
561 |
vma_size = (vma->vm_end - vma->vm_start); |
|
562 |
||
563 |
if (kmem_entry->refs&KMEM_REF_COUNT) { |
|
564 |
kmem_entry->refs -= vma_size / PAGE_SIZE; |
|
565 |
}
|
|
76
by Suren A. Chilingaryan
Handle correctly reference counting in the driver |
566 |
}
|
73
by Suren A. Chilingaryan
Implement DMA access synchronization in the driver |
567 |
}
|
568 |
||
569 |
static struct vm_operations_struct pcidriver_kmem_mmap_ops = { |
|
570 |
.close = pcidriver_kmem_mmap_close |
|
571 |
};
|
|
572 |
||
71
by Suren A. Chilingaryan
First iteration of work to preserve DMA state between executions |
573 |
/**
|
574 |
*
|
|
1
by Suren A. Chilingaryan
Initial import |
575 |
* mmap() kernel memory to userspace.
|
576 |
*
|
|
577 |
*/
|
|
578 |
int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *vma) |
|
579 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
580 |
unsigned long vma_size; |
581 |
pcidriver_kmem_entry_t *kmem_entry; |
|
582 |
int ret; |
|
583 |
||
584 |
mod_info_dbg("Entering mmap_kmem\n"); |
|
585 |
||
586 |
/* FIXME: Is this really right? Always just the latest one? Can't we identify one? */
|
|
587 |
/* Get latest entry on the kmem_list */
|
|
588 |
kmem_entry = pcidriver_kmem_find_entry_id(privdata, privdata->kmem_cur_id); |
|
589 |
if (!kmem_entry) { |
|
590 |
mod_info("Trying to mmap a kernel memory buffer without creating it first!\n"); |
|
591 |
return -EFAULT; |
|
592 |
}
|
|
593 |
||
594 |
mod_info_dbg("Got kmem_entry with id: %d\n", kmem_entry->id); |
|
595 |
||
596 |
/* Check sizes */
|
|
597 |
vma_size = (vma->vm_end - vma->vm_start); |
|
598 |
||
599 |
if ((vma_size > kmem_entry->size) && |
|
600 |
((kmem_entry->size < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { |
|
601 |
mod_info("kem_entry size(%lu) and vma size do not match(%lu)\n", kmem_entry->size, vma_size); |
|
602 |
return -EINVAL; |
|
603 |
}
|
|
604 |
||
605 |
/* reference counting */
|
|
606 |
if ((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)&&(kmem_entry->refs&KMEM_REF_COUNT)) { |
|
607 |
mod_info("can't make second mmaping for exclusive kmem_entry\n"); |
|
608 |
return -EBUSY; |
|
609 |
}
|
|
610 |
if (((kmem_entry->refs&KMEM_REF_COUNT) + (vma_size / PAGE_SIZE)) > KMEM_REF_COUNT) { |
|
611 |
mod_info("maximal amount of references is reached by kmem_entry\n"); |
|
612 |
return -EBUSY; |
|
613 |
}
|
|
614 |
||
615 |
kmem_entry->refs += vma_size / PAGE_SIZE; |
|
616 |
||
617 |
vma->vm_flags |= (VM_RESERVED); |
|
618 |
||
407
by Suren A. Chilingaryan
Only set pgprot_noncached for consistent buffers |
619 |
if ((kmem_entry->type&&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_CONSISTENT) { |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
620 |
// This is coherent memory, so it must not be cached.
|
407
by Suren A. Chilingaryan
Only set pgprot_noncached for consistent buffers |
621 |
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
622 |
}
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
623 |
|
624 |
mod_info_dbg("Mapping address %08lx / PFN %08lx\n", |
|
625 |
virt_to_phys((void*)kmem_entry->cpua), |
|
626 |
page_to_pfn(virt_to_page((void*)kmem_entry->cpua))); |
|
627 |
||
628 |
if ((kmem_entry->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) { |
|
365
by Suren A. Chilingaryan
Restructure driver headers |
629 |
ret = remap_pfn_range(vma, vma->vm_start, (kmem_entry->dma_handle >> PAGE_SHIFT), (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
630 |
} else { |
365
by Suren A. Chilingaryan
Restructure driver headers |
631 |
ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page((void*)(kmem_entry->cpua))), (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
632 |
}
|
633 |
||
634 |
if (ret) { |
|
635 |
mod_info("kmem remap failed: %d (%lx)\n", ret,kmem_entry->cpua); |
|
636 |
kmem_entry->refs -= 1; |
|
637 |
return -EAGAIN; |
|
638 |
}
|
|
639 |
||
640 |
vma->vm_ops = &pcidriver_kmem_mmap_ops; |
|
641 |
vma->vm_private_data = (void*)kmem_entry; |
|
642 |
||
643 |
return ret; |
|
1
by Suren A. Chilingaryan
Initial import |
644 |
}
|