/alps/pcitool

To get this branch, use:
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
}