From: Guillermo Marcus on
Hi all,

I recently run with the following situation while developing a PCI
driver. The driver allocates memory for a PCI device using
pci_alloc_consistent as this memory is going to be used to perform DMA
transfers. To pass the data from/to the user application, I mmap the
buffer into userspace. However, if I try to use remap_pfn_range
(>=2.6.10) or the older remap_page_range(<=2.6.9) for mmaping, it ends
up creating a new buffer, because they do not support RAM mapping, then
pagefaulting to the VMA and by default allocating new pages. Therefore,
I had to implement the nopage method and mmap one page at a time as they
fault.

However, to my point of view, this is unnecessary. The memory is already
allocated, the memory is locked because it is consistent, and it may be
a (very small) performance and stability issue to do them one-by-one.
Why can't I simply mmap it all at once? am I missing some function? More
important, why can't remap_{pfn/page}_range handle it?


Best wishes,
Guillermo Marcus

Note: I am using kernel 2.6.9 for these tests, as it is required by my
current setup. Maybe this issue has already been addressed in newer
kernel. If that is the case, please let me know.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Jiri Slaby on
Guillermo Marcus wrote:
> Hi all,
>
> I recently run with the following situation while developing a PCI
> driver. The driver allocates memory for a PCI device using
> pci_alloc_consistent as this memory is going to be used to perform DMA
> transfers. To pass the data from/to the user application, I mmap the
> buffer into userspace. However, if I try to use remap_pfn_range
> (>=2.6.10) or the older remap_page_range(<=2.6.9) for mmaping, it ends
> up creating a new buffer, because they do not support RAM mapping, then
> pagefaulting to the VMA and by default allocating new pages. Therefore,
> I had to implement the nopage method and mmap one page at a time as they
> fault.
>
> However, to my point of view, this is unnecessary. The memory is already
> allocated, the memory is locked because it is consistent, and it may be
> a (very small) performance and stability issue to do them one-by-one.
> Why can't I simply mmap it all at once? am I missing some function? More
> important, why can't remap_{pfn/page}_range handle it?

Piece of code please. pci_alloc_consistent calls __get_free_pages, and there
should be no problem with mmaping this area.

regards,
--
http://www.fi.muni.cz/~xslaby/ Jiri Slaby
faculty of informatics, masaryk university, brno, cz
e-mail: jirislaby gmail com, gpg pubkey fingerprint:
B674 9967 0407 CE62 ACC8 22A0 32CC 55C3 39D4 7A7E
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Rolf Offermanns on
Guillermo Marcus wrote:
> I recently run with the following situation while developing a PCI
> driver. The driver allocates memory for a PCI device using
> pci_alloc_consistent as this memory is going to be used to perform DMA
> transfers. To pass the data from/to the user application, I mmap the
> buffer into userspace. However, if I try to use remap_pfn_range
> (>=2.6.10) or the older remap_page_range(<=2.6.9) for mmaping, it ends
> up creating a new buffer, because they do not support RAM mapping, then
> pagefaulting to the VMA and by default allocating new pages. Therefore,
> I had to implement the nopage method and mmap one page at a time as they
> fault.
>
> However, to my point of view, this is unnecessary. The memory is already
> allocated, the memory is locked because it is consistent, and it may be
> a (very small) performance and stability issue to do them one-by-one.
> Why can't I simply mmap it all at once? am I missing some function? More
> important, why can't remap_{pfn/page}_range handle it?
>
Here is what I did some time ago:

-> Reserve mem at boot time (mem=realmem-size_of_mem_you_need) / bigphysmem
-> I used the highmem allocator from the LDD2/3 examples to get a pointer
the this reserved memory at runtime.
-> Use ioremap() to remap the memory to kernelspace
-> do some magic (I don't remember the background, sorry) with the vma_flags
in your mmap() function:

vma->vm_flags |= VM_RESERVED;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

and then do a remap_pfn_range() as usualy.

HTH,
Rolf


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Guillermo Marcus on
Hi Jiri,

The fact that it does not works with RAM is well documented in LDD3,
pages 430++. It says (and I tested) that remap_xxx_range does not work
in this case. They suggest a method using nopage, similar to the one I
implement.

I do not see why remap_xxx_range has the limitation, but it is there.
The question is then: can the limitation be removed, or can we implement
a new function that maps RAM all at once without the need for a nopage
implementation?

In any case, here is the code.

Best Wishes,
Guillermo


/*************************************************/

To allocate (inside an IOctl cmd):

....
retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size,
&(kmem_entry->dma_handle) );
if (retptr == NULL)
goto kmem_alloc_mem_fail;
kmem_entry->cpua = (unsigned long)retptr;
kmem_entry->size = kmem_handle->size;
kmem_entry->id = atomic_inc_return(&privdata->kmem_count) - 1;
....


To mmap (inside mmap fops, this DOES NOT works):

....
/* Map the Buffer to the VMA */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
ret = remap_pfn_range(
vma,
vma->vm_start,
__pa(kmem_entry->cpua) >> PAGE_SHIFT,
kmem_entry->size,
vma->vm_page_prot );
#else
ret = remap_page_range(
vma,
vma->vm_start,
__pa(kmem_entry->cpua),
kmem_entry->size,
vma->vm_page_prot );
#endif
....


Forcing me to this:

(in mmap):

....
/* Set VM operations */
vma->vm_private_data = kmem_entry;
vma->vm_ops = &pcidriver_vm_operations;
pcidriver_vm_open(vma);
....

(and the nopage vm_ops):

....
/* Maps physical memory to user space */
struct page *pcidriver_vm_nopage(struct vm_area_struct *vma, unsigned
long address, int * type) {

pcidriver_kmem_entry_t *kmem_entry;
struct page *page = NOPAGE_SIGBUS;
unsigned long pfn_offset,pfn;

/* Get private data for the page */
kmem_entry = vma->vm_private_data;

/* All checks where done during the mmap_kmem call, so we can safely
* just map the offset of the vm area to the offset of the region,
* that is guaranteed to be contiguos */

pfn_offset = (address) - (vma->vm_start);
pfn = (__pa(kmem_entry->cpua) + pfn_offset ) >> PAGE_SHIFT;

if (!pfn_valid(pfn)) {
mod_info("Invalid pfn in nopage() - 0x%lx \n", pfn);
return NOPAGE_SIGBUS;
}

page = pfn_to_page( pfn );

get_page(page);
if (type)
*type = VM_FAULT_MINOR;

return page;
}
....

/*************************************************/



Jiri Slaby wrote:
> Guillermo Marcus wrote:
>> Hi all,
>>
>> I recently run with the following situation while developing a PCI
>> driver. The driver allocates memory for a PCI device using
>> pci_alloc_consistent as this memory is going to be used to perform DMA
>> transfers. To pass the data from/to the user application, I mmap the
>> buffer into userspace. However, if I try to use remap_pfn_range
>> (>=2.6.10) or the older remap_page_range(<=2.6.9) for mmaping, it ends
>> up creating a new buffer, because they do not support RAM mapping, then
>> pagefaulting to the VMA and by default allocating new pages. Therefore,
>> I had to implement the nopage method and mmap one page at a time as they
>> fault.
>>
>> However, to my point of view, this is unnecessary. The memory is already
>> allocated, the memory is locked because it is consistent, and it may be
>> a (very small) performance and stability issue to do them one-by-one.
>> Why can't I simply mmap it all at once? am I missing some function? More
>> important, why can't remap_{pfn/page}_range handle it?
>
> Piece of code please. pci_alloc_consistent calls __get_free_pages, and there
> should be no problem with mmaping this area.
>
> regards,
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Guillermo Marcus on
Hi Rolf,

Thanks for your comments. Unfortunately, given some of the platforms we
want to support, I cannot reserve memory at boot time, so I had to find
some other way. Besides, it is anyway interesting to see how to present
a buffer allocated using the DMA interfaces (pci_alloc or dma_alloc) to
the user space.

All the best,
Guillermo

Rolf Offermanns wrote:
> Guillermo Marcus wrote:
>> I recently run with the following situation while developing a PCI
>> driver. The driver allocates memory for a PCI device using
>> pci_alloc_consistent as this memory is going to be used to perform DMA
>> transfers. To pass the data from/to the user application, I mmap the
>> buffer into userspace. However, if I try to use remap_pfn_range
>> (>=2.6.10) or the older remap_page_range(<=2.6.9) for mmaping, it ends
>> up creating a new buffer, because they do not support RAM mapping, then
>> pagefaulting to the VMA and by default allocating new pages. Therefore,
>> I had to implement the nopage method and mmap one page at a time as they
>> fault.
>>
>> However, to my point of view, this is unnecessary. The memory is already
>> allocated, the memory is locked because it is consistent, and it may be
>> a (very small) performance and stability issue to do them one-by-one.
>> Why can't I simply mmap it all at once? am I missing some function? More
>> important, why can't remap_{pfn/page}_range handle it?
>>
> Here is what I did some time ago:
>
> -> Reserve mem at boot time (mem=realmem-size_of_mem_you_need) / bigphysmem
> -> I used the highmem allocator from the LDD2/3 examples to get a pointer
> the this reserved memory at runtime.
> -> Use ioremap() to remap the memory to kernelspace
> -> do some magic (I don't remember the background, sorry) with the vma_flags
> in your mmap() function:
>
> vma->vm_flags |= VM_RESERVED;
> vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>
> and then do a remap_pfn_range() as usualy.
>
> HTH,
> Rolf
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo(a)vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/