4
* @brief This file contains all functions dealing with kernel memory.
5
* @author Guillermo Marcus
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>
18
#include <linux/pagemap.h>
20
#include "config.h" /* compile-time configuration */
21
#include "compat.h" /* compatibility definitions for older linux */
22
#include "pciDriver.h" /* external interface for the driver */
23
#include "common.h" /* internal definitions for all parts */
24
#include "kmem.h" /* prototypes for kernel memory */
25
#include "sysfs.h" /* prototypes for sysfs */
27
/* VM_RESERVED is removed in 3.7-rc1 */
29
# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
34
* Allocates new kernel memory including the corresponding management structure, makes
35
* it available via sysfs if possible.
38
int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle)
41
pcidriver_kmem_entry_t *kmem_entry;
44
if (kmem_handle->flags&KMEM_FLAG_REUSE) {
45
kmem_entry = pcidriver_kmem_find_entry_use(privdata, kmem_handle->use, kmem_handle->item);
47
unsigned long flags = kmem_handle->flags;
49
if (flags&KMEM_FLAG_TRY) {
50
kmem_handle->type = kmem_entry->type;
51
kmem_handle->size = kmem_entry->size;
52
kmem_handle->align = kmem_entry->align;
54
if (kmem_handle->type != kmem_entry->type) {
55
mod_info("Invalid type of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->type, kmem_handle->type);
59
if ((kmem_handle->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_PAGE) {
60
kmem_handle->size = kmem_entry->size;
61
} else if (kmem_handle->size != kmem_entry->size) {
62
mod_info("Invalid size of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->size, kmem_handle->size);
66
if (kmem_handle->align != kmem_entry->align) {
67
mod_info("Invalid alignment of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->align, kmem_handle->align);
71
if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?1:0) != ((flags&KMEM_FLAG_EXCLUSIVE)?1:0)) {
72
mod_info("Invalid mode of reusable kmem_entry\n");
78
if ((kmem_entry->mode&KMEM_MODE_COUNT)==KMEM_MODE_COUNT) {
79
mod_info("Reuse counter of kmem_entry is overflown");
84
kmem_handle->handle_id = kmem_entry->id;
85
kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
87
kmem_handle->flags = KMEM_FLAG_REUSED;
88
if (kmem_entry->refs&KMEM_REF_HW) kmem_handle->flags |= KMEM_FLAG_REUSED_HW;
89
if (kmem_entry->mode&KMEM_MODE_PERSISTENT) kmem_handle->flags |= KMEM_FLAG_REUSED_PERSISTENT;
91
kmem_entry->mode += 1;
92
if (flags&KMEM_FLAG_HW) {
93
if ((kmem_entry->refs&KMEM_REF_HW)==0)
94
pcidriver_module_get(privdata);
96
kmem_entry->refs |= KMEM_REF_HW;
98
if (flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT;
100
privdata->kmem_cur_id = kmem_entry->id;
105
if (kmem_handle->flags&KMEM_FLAG_TRY) return -ENOENT;
108
/* First, allocate zeroed memory for the kmem_entry */
109
if ((kmem_entry = kcalloc(1, sizeof(pcidriver_kmem_entry_t), GFP_KERNEL)) == NULL)
110
goto kmem_alloc_entry_fail;
112
/* Initialize the kmem_entry */
113
kmem_entry->id = atomic_inc_return(&privdata->kmem_count) - 1;
114
privdata->kmem_cur_id = kmem_entry->id;
115
kmem_handle->handle_id = kmem_entry->id;
117
kmem_entry->use = kmem_handle->use;
118
kmem_entry->item = kmem_handle->item;
119
kmem_entry->type = kmem_handle->type;
120
kmem_entry->align = kmem_handle->align;
121
kmem_entry->direction = PCI_DMA_NONE;
123
/* Initialize sysfs if possible */
124
if (pcidriver_sysfs_initialize_kmem(privdata, kmem_entry->id, &(kmem_entry->sysfs_attr)) != 0)
125
goto kmem_alloc_mem_fail;
127
/* ...and allocate the DMA memory */
128
/* note this is a memory pair, referencing the same area: the cpu address (cpua)
129
* and the PCI bus address (pa). The CPU and PCI addresses may not be the same.
130
* The CPU sees only CPU addresses, while the device sees only PCI addresses.
131
* CPU address is used for the mmap (internal to the driver), and
132
* PCI address is the address passed to the DMA Controller in the device.
134
switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) {
135
case PCILIB_KMEM_TYPE_CONSISTENT:
136
retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) );
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;
147
case PCILIB_KMEM_TYPE_PAGE:
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;
155
retptr = (void*)__get_free_pages(flags, get_order(kmem_handle->size));
156
kmem_entry->dma_handle = 0;
159
if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) {
160
kmem_entry->direction = PCI_DMA_TODEVICE;
161
kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_TODEVICE);
162
if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) {
163
free_pages((unsigned long)retptr, get_order(kmem_handle->size));
164
goto kmem_alloc_mem_fail;
166
} else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) {
167
kmem_entry->direction = PCI_DMA_FROMDEVICE;
168
kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_FROMDEVICE);
169
if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) {
170
free_pages((unsigned long)retptr, get_order(kmem_handle->size));
171
goto kmem_alloc_mem_fail;
179
goto kmem_alloc_mem_fail;
184
goto kmem_alloc_mem_fail;
186
kmem_entry->size = kmem_handle->size;
187
kmem_entry->cpua = (unsigned long)retptr;
188
kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
190
kmem_entry->mode = 1;
191
if (kmem_handle->flags&KMEM_FLAG_REUSE) {
192
kmem_entry->mode |= KMEM_MODE_REUSABLE;
193
if (kmem_handle->flags&KMEM_FLAG_EXCLUSIVE) kmem_entry->mode |= KMEM_MODE_EXCLUSIVE;
194
if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT;
197
kmem_entry->refs = 0;
198
if (kmem_handle->flags&KMEM_FLAG_HW) {
199
pcidriver_module_get(privdata);
201
kmem_entry->refs |= KMEM_REF_HW;
204
kmem_handle->flags = 0;
206
set_pages_reserved_compat(kmem_entry->cpua, kmem_entry->size);
208
/* Add the kmem_entry to the list of the device */
209
spin_lock( &(privdata->kmemlist_lock) );
210
list_add_tail( &(kmem_entry->list), &(privdata->kmem_list) );
211
spin_unlock( &(privdata->kmemlist_lock) );
217
kmem_alloc_entry_fail:
221
static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle, pcidriver_kmem_entry_t *kmem_entry) {
222
if ((kmem_handle->flags & KMEM_FLAG_FORCE) == 0) {
223
if (kmem_entry->mode&KMEM_MODE_COUNT)
224
kmem_entry->mode -= 1;
226
if (kmem_handle->flags&KMEM_FLAG_HW) {
227
if (kmem_entry->refs&KMEM_REF_HW)
228
pcidriver_module_put(privdata);
230
kmem_entry->refs &= ~KMEM_REF_HW;
233
if (kmem_handle->flags&KMEM_FLAG_PERSISTENT)
234
kmem_entry->mode &= ~KMEM_MODE_PERSISTENT;
236
if (kmem_handle->flags&KMEM_FLAG_REUSE)
239
if (kmem_entry->refs) {
240
kmem_entry->mode += 1;
241
mod_info("can't free referenced kmem_entry\n");
245
if (kmem_entry->mode & KMEM_MODE_PERSISTENT) {
246
kmem_entry->mode += 1;
247
mod_info("can't free persistent kmem_entry\n");
251
if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0))
254
if (kmem_entry->refs&KMEM_REF_HW)
255
pcidriver_module_put(privdata);
257
while (!atomic_add_negative(-1, &(privdata->refs))) pcidriver_module_put(privdata);
258
atomic_inc(&(privdata->refs));
265
static int pcidriver_kmem_free_use(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle)
269
struct list_head *ptr, *next;
270
pcidriver_kmem_entry_t *kmem_entry;
272
/* iterate safely over the entries and delete them */
273
list_for_each_safe(ptr, next, &(privdata->kmem_list)) {
274
kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
275
if (kmem_entry->use == kmem_handle->use) {
276
err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry);
278
pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */
285
mod_info("Some kmem_entries for use %lx are still referenced\n", kmem_handle->use);
294
* Called via sysfs, frees kernel memory and the corresponding management structure
297
int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle )
300
pcidriver_kmem_entry_t *kmem_entry;
302
if (kmem_handle->flags&KMEM_FLAG_MASS) {
303
kmem_handle->flags &= ~KMEM_FLAG_MASS;
304
return pcidriver_kmem_free_use(privdata, kmem_handle);
307
/* Find the associated kmem_entry for this buffer */
308
if ((kmem_entry = pcidriver_kmem_find_entry(privdata, kmem_handle)) == NULL)
309
return -EINVAL; /* kmem_handle is not valid */
311
err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry);
314
return pcidriver_kmem_free_entry(privdata, kmem_entry);
321
* Called when cleaning up, frees all kernel memory and their corresponding management structure
324
int pcidriver_kmem_free_all(pcidriver_privdata_t *privdata)
327
struct list_head *ptr, *next;
328
pcidriver_kmem_entry_t *kmem_entry;
330
/* iterate safely over the entries and delete them */
331
list_for_each_safe(ptr, next, &(privdata->kmem_list)) {
332
kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
333
/*if (kmem_entry->refs)
336
pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */
340
mod_info("Some kmem_entries are still referenced\n");
350
* Synchronize memory to/from the device (or in both directions).
353
int pcidriver_kmem_sync_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry, int direction)
355
if (kmem_entry->direction == PCI_DMA_NONE)
358
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
360
case PCILIB_KMEM_SYNC_TODEVICE:
361
pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
363
case PCILIB_KMEM_SYNC_FROMDEVICE:
364
pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
366
case PCILIB_KMEM_SYNC_BIDIRECTIONAL:
367
pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
368
pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
371
return -EINVAL; /* wrong direction parameter */
375
case PCILIB_KMEM_SYNC_TODEVICE:
376
pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
378
case PCILIB_KMEM_SYNC_FROMDEVICE:
379
pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
381
case PCILIB_KMEM_SYNC_BIDIRECTIONAL:
382
pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
385
return -EINVAL; /* wrong direction parameter */
389
return 0; /* success */
394
* Synchronize memory to/from the device (or in both directions).
397
int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync )
399
pcidriver_kmem_entry_t *kmem_entry;
401
/* Find the associated kmem_entry for this buffer */
402
if ((kmem_entry = pcidriver_kmem_find_entry(privdata, &(kmem_sync->handle))) == NULL)
403
return -EINVAL; /* kmem_handle is not valid */
405
return pcidriver_kmem_sync_entry(privdata, kmem_entry, kmem_sync->dir);
410
* Free the given kmem_entry and its memory.
413
int pcidriver_kmem_free_entry(pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry)
415
pcidriver_sysfs_remove(privdata, &(kmem_entry->sysfs_attr));
417
/* Go over the pages of the kmem buffer, and mark them as not reserved */
419
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
421
* This code is DISABLED.
422
* Apparently, it is not needed to unreserve them. Doing so here
423
* hangs the machine. Why?
427
* http://lwn.net/Articles/161204/
428
* http://lists.openfabrics.org/pipermail/general/2007-March/034101.html
430
* I insist, this should be enabled, but doing so hangs the machine.
431
* Literature supports the point, and there is even a similar problem (see link)
432
* But this is not the case. It seems right to me. but obviously is not.
434
* Anyway, this goes away in kernel >=2.6.15.
436
unsigned long start = __pa(kmem_entry->cpua) >> PAGE_SHIFT;
437
unsigned long end = __pa(kmem_entry->cpua + kmem_entry->size) >> PAGE_SHIFT;
439
for(i=start;i<end;i++) {
440
struct page *kpage = pfn_to_page(i);
441
ClearPageReserved(kpage);
446
/* Release DMA memory */
447
switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) {
448
case PCILIB_KMEM_TYPE_CONSISTENT:
449
pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle );
451
case PCILIB_KMEM_TYPE_REGION:
452
iounmap((void *)(kmem_entry->cpua));
454
case PCILIB_KMEM_TYPE_PAGE:
455
if (kmem_entry->dma_handle) {
456
if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) {
457
pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE);
458
} else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) {
459
pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_FROMDEVICE);
462
free_pages((unsigned long)kmem_entry->cpua, get_order(kmem_entry->size));
467
/* Remove the kmem list entry */
468
spin_lock( &(privdata->kmemlist_lock) );
469
list_del( &(kmem_entry->list) );
470
spin_unlock( &(privdata->kmemlist_lock) );
472
/* Release kmem_entry memory */
480
* Find the corresponding kmem_entry for the given kmem_handle.
483
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle)
485
struct list_head *ptr;
486
pcidriver_kmem_entry_t *entry, *result = NULL;
488
/* should I implement it better using the handle_id? */
490
spin_lock(&(privdata->kmemlist_lock));
491
list_for_each(ptr, &(privdata->kmem_list)) {
492
entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
494
if (entry->id == kmem_handle->handle_id) {
500
spin_unlock(&(privdata->kmemlist_lock));
506
* find the corresponding kmem_entry for the given id.
509
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id(pcidriver_privdata_t *privdata, int id)
511
struct list_head *ptr;
512
pcidriver_kmem_entry_t *entry, *result = NULL;
514
spin_lock(&(privdata->kmemlist_lock));
515
list_for_each(ptr, &(privdata->kmem_list)) {
516
entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
518
if (entry->id == id) {
524
spin_unlock(&(privdata->kmemlist_lock));
530
* find the corresponding kmem_entry for the given use and item.
533
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_use(pcidriver_privdata_t *privdata, unsigned long use, unsigned long item)
535
struct list_head *ptr;
536
pcidriver_kmem_entry_t *entry, *result = NULL;
538
spin_lock(&(privdata->kmemlist_lock));
539
list_for_each(ptr, &(privdata->kmem_list)) {
540
entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
542
if ((entry->use == use)&&(entry->item == item)&&(entry->mode&KMEM_MODE_REUSABLE)) {
548
spin_unlock(&(privdata->kmemlist_lock));
553
void pcidriver_kmem_mmap_close(struct vm_area_struct *vma) {
554
unsigned long vma_size;
555
pcidriver_kmem_entry_t *kmem_entry = (pcidriver_kmem_entry_t*)vma->vm_private_data;
558
if (kmem_entry->id == 0) {
559
mod_info("refs: %p %p %lx\n", vma, vma->vm_private_data, kmem_entry->refs);
560
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);
564
vma_size = (vma->vm_end - vma->vm_start);
566
if (kmem_entry->refs&KMEM_REF_COUNT) {
567
kmem_entry->refs -= vma_size / PAGE_SIZE;
572
static struct vm_operations_struct pcidriver_kmem_mmap_ops = {
573
.close = pcidriver_kmem_mmap_close
578
* mmap() kernel memory to userspace.
581
int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *vma)
583
unsigned long vma_size;
584
pcidriver_kmem_entry_t *kmem_entry;
587
mod_info_dbg("Entering mmap_kmem\n");
589
/* FIXME: Is this really right? Always just the latest one? Can't we identify one? */
590
/* Get latest entry on the kmem_list */
591
kmem_entry = pcidriver_kmem_find_entry_id(privdata, privdata->kmem_cur_id);
593
mod_info("Trying to mmap a kernel memory buffer without creating it first!\n");
597
mod_info_dbg("Got kmem_entry with id: %d\n", kmem_entry->id);
600
vma_size = (vma->vm_end - vma->vm_start);
602
if ((vma_size > kmem_entry->size) &&
603
((kmem_entry->size < PAGE_SIZE) && (vma_size != PAGE_SIZE))) {
604
mod_info("kem_entry size(%lu) and vma size do not match(%lu)\n", kmem_entry->size, vma_size);
608
/* reference counting */
609
if ((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)&&(kmem_entry->refs&KMEM_REF_COUNT)) {
610
mod_info("can't make second mmaping for exclusive kmem_entry\n");
613
if (((kmem_entry->refs&KMEM_REF_COUNT) + (vma_size / PAGE_SIZE)) > KMEM_REF_COUNT) {
614
mod_info("maximal amount of references is reached by kmem_entry\n");
618
kmem_entry->refs += vma_size / PAGE_SIZE;
620
vma->vm_flags |= (VM_RESERVED);
622
#ifdef pgprot_noncached
623
// This is coherent memory, so it must not be cached.
624
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
627
mod_info_dbg("Mapping address %08lx / PFN %08lx\n",
628
virt_to_phys((void*)kmem_entry->cpua),
629
page_to_pfn(virt_to_page((void*)kmem_entry->cpua)));
631
if ((kmem_entry->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) {
632
ret = remap_pfn_range_compat(
635
kmem_entry->dma_handle,
636
(vma_size < kmem_entry->size)?vma_size:kmem_entry->size,
639
ret = remap_pfn_range_cpua_compat(
643
(vma_size < kmem_entry->size)?vma_size:kmem_entry->size,
648
mod_info("kmem remap failed: %d (%lx)\n", ret,kmem_entry->cpua);
649
kmem_entry->refs -= 1;
653
vma->vm_ops = &pcidriver_kmem_mmap_ops;
654
vma->vm_private_data = (void*)kmem_entry;