DMA in user space (uio dma) //code analysis
2011-11-02 19:35
393 查看
Joseph (Honggang Yang)<ganggexiongqi@gmail.com>
Contents: uio-dma.c code review (V1.3)
Date: 11-02-2011
Last modified: 11-02-2011
-----------------------------------------------------------------------------------------------------------
Distributed and Embedded System Lab (分布式嵌入式系统实验室,兰州大学)
===============================================================
1. function list
2. function analysis
3. related structures
4. the source code
===============
1. function list
|- function
|| uio_dma_device_lock +
|| uio_dma_device_unlock +
|| __drop_dev_mappings +
|| uio_dma_device_free +
|| uio_dma_device_get +
|| uio_dma_device_put +
|| __device_lookup +
|| uio_dma_device_lookup +
|| uio_dma_device_open ======= ++++
|| uio_dma_device_close ====== ++++
|| __last_page +
|| uio_dma_area_free +
|| uio_dma_area_alloc +
|| uio_dma_area_get +
|| uio_dma_area_put +
|| uio_dma_area_lookup +
|| uio_dma_mapping_del +
|| uio_dma_mapping_add +
|| uio_dma_mapping_lookup +
|| uio_dma_context_lock +
|| uio_dma_context_unlock +
|| append_to +
|| uio_dma_cmd_alloc +
|| uio_dma_cmd_free +
|| uio_dma_cmd_map +
|| uio_dma_cmd_unmap +
|| uio_dma_ioctl +
|| __drop_ctx_areas +
|| uio_dma_close +
|| uio_dma_open +
|| uio_dma_poll +
|| uio_dma_read +
|| uio_dma_write +
|| uio_dma_vm_open +
|| uio_dma_vm_close +
|| uio_dma_vm_fault +
|| uio_dma_mmap +
|| uio_dma_init_module +
|| uio_dma_exit_module +
==============================
2. function analysis
1.
F: Register the misc device "uio-dma"
static int __init uio_dma_init_module(void)
Init device lists //INIT_LIST_HEAD
Init mutex used to protect uio_dma_dev_list//mutex_init
Register our misc device //misc_register[misc device is a special char dev]
2.
F: Unregister the misc device "uio-dma"
static void __exit uio_dma_exit_module(void)
//misc_deregister
3.
F: allocate a new uio-dma context and attached to the fd.
static int uio_dma_open(struct inode *inode, struct file * file)
/* Allocate new uio-dma context */ //kzalloc
Attach the uio-dma context with a fd.
4.
F: Try to free all the memory allocate under the context attached with
the fd which @inode belong to.
static int uio_dma_close(struct inode *inode, struct file *file)
As to the areas in context @uc, decrease their ref. counter.
If the counter become 0, "free" the areas. //__drop_ctx_areas
5.
F: As to the areas in context @uc, decrease their ref. counter.
If the counter become 0, "free" the areas.
static void __drop_ctx_areas(struct uio_dma_context *uc)
As to the areas in context @uc, decrease their ref. counter.
If the counter become 0, "free" the areas. //uio_dma_area_put
6.
F: Decrease @area's ref. counter, if counter become 0, "Free" the area.
static void uio_dma_area_put(struct uio_dma_area *area)
atomicly decrease @area's ref. counter.
if no one use it, then Release the area and related memory chunks.
//atomic_dec_and_test, uio_dma_area_free
7.
F:Release DMA area.
/*
* Release DMA area.
* Called only after all references to this area have been dropped.
*/
static void uio_dma_area_free(struct uio_dma_area *area)
Clear page Resered flag of all pages of all chunk belong to @area
and free the pages //__last_page, virt_to_page, ClearPageReserved
free the @area.
8.
F:get last page of the region dercribed by @addr and @size
static inline struct page *__last_page(void *addr, unsigned long size)
get last page of the region dercribed by @addr and @size//virt_to_page
9.
F:Fina a DMA area, map it to userspace.
static int uio_dma_mmap(struct file *file, struct vm_area_struct *vma)
Find a DMA area by offset(!We have allocate DMA area in uio_dma_ioctl()) //uio_dma_area_lookup
If this map is a partial mappings, reject it.// You can only map the whole dma area in u space.
Set the @vma's cache property indicated by struct uio_dma_area's cache member.
Create page table for every chunk in the dma area //page_to_pfn, virt_to_page, remap_pfn_range
Override the @vma's operations(do nothing here!) //vma->vm_ops = &uio_dma_mmap_ops;
NOTE:
In userspace:
mmap(NULL, da->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, da->mmap_offset);
In uio_dma_mmap():
unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
...
// Find an area that matches the offset and mmap it.
area = uio_dma_area_lookup(uc, offset);
So we can draw a conclusion:
Member of struct uio_dma_area 'mmap_offset' is in page unit.
10.
F:Look up DMA area by offset.
/*
* Look up DMA area by offset.
* Must be called under context mutex.
*/
static struct uio_dma_area *uio_dma_area_lookup(struct uio_dma_context *uc, uint64_t offset)
11.
F:Do differenct opration depends on the @cmd
(UIO_DMA_ALLOC, UIO_DMA_FREE, UIO_DMA_MAP, UIO_DMA_UNMAP)
static long uio_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Get uio_dma_context lock//uio_dma_context_lock
Do differenct opration depends on the @cmd // @cmd is passed from userspace ioctl()
Free uio_dma_context lock//uio_dma_context_unlock
12.
F:
static int uio_dma_cmd_alloc(struct uio_dma_context *uc, void __user *argp)
Copy the userspace's parameter(a uio_dma_alloc_req)//copy_from_user
Round up the chunk size to PAGE unit //roundup
Find list_head to append new area to //append_to
Allocate new DMA area, save every chunk's first page's kernel virtual address
in @area->addr[i] //uio_dma_area_alloc
/* Add to the context */
copy the @area's contents to userspace //copy_to_user
13.
F: return list_head to append new area to
/*
* Make sure new area (offset & size) does not everlap with
* the existing areas and return list_head to append new area to.
* Must be called under context mutex.
*/
static struct list_head *append_to(struct uio_dma_context *uc,
uint64_t *offset, unsigned int size)
14.
F:Allocate new DMA area, save every chunk's first page's kernel virtual address in @area->addr[i]
/*
* Allocate new DMA area.
*/
static struct uio_dma_area *uio_dma_area_alloc(uint64_t dma_mask, unsigned int memnode,
unsigned int cache, unsigned int chunk_size, unsigned int chunk_count)
allocate a uio_dma_area varible //kzalloc()
init the uio_dma_area varible
allocate pages for every chunk and save the (kernel virtual) address of
the first page in area->addr[i], set the last page's 'Reserved' flag//alloc_pages_node(),page_address()
return the uio_dma_area varible's pointer
15.
F: Delete dma area from @uc.area list and release the DMA area
static int uio_dma_cmd_free(struct uio_dma_context *uc, void __user *argp)
copy the uio_dma_free_req type variable from the u space//copy_from_user
Look up DMA area by offset //uio_dma_area_lookup
delete the dma area from the @uc.area list //list_del
Release DMA area // uio_dma_area_put
16.
F: Release DMA area
static void uio_dma_area_put(struct uio_dma_area *area)
Release DMA area //uio_dma_area_free, atomic_dec_and_test
17.
F: Create DMA mapping (it attaches uio dma area @argp.mmap_offset and uio dma device @argp.devid)
static int uio_dma_cmd_map(struct uio_dma_context *uc, void __user *argp)
copy the contents of the uio_dma_map_req variable from U space to @req //copy_from_user
find the uio dma area by @req.mmap_offset //uio_dma_area_lookup
Lookup UIO DMA device based by id, increments device refcount if found
and return pointer to it. //uio_dma_device_lookup
get the device's lock //uio_dma_device_lock
Look up DMA mapping by area and direction.//uio_dma_mapping_lookup
If the DMA mapping is not created before,
then, create a new DMA mapping attached to device @ud//uio_dma_mapping_add
//req.chunk_count = area->chunk_count; NOTE: the request dma map's chunk size and chunk_count may be changed
//req.chunk_size = area->chunk_size;
Copy the @req's contents to U space //copy_to_user
18.
F: Lookup UIO DMA device based by id, increments device refcount if found
and return pointer to it.
/*
* Lookup device by uio dma id.
* Caller must drop the reference to the returned
* device when it's done with it.
*/
static struct uio_dma_device* uio_dma_device_lookup(uint32_t id)
get lock uio_dma_dev_mutex //mutex_lock
Lookup UIO DMA device based by id, increments device refcount if found.//__device_lookup
free lock uio_dma_dev_mutex //mutex_unlock
19.
F:Lookup UIO DMA device based by id, increments device refcount if found.
/*
* Lookup UIO DMA device based by id.
* Must be called under uio_dma_dev_mutex.
* Increments device refcount if found.
*/
static struct uio_dma_device* __device_lookup(uint32_t id)
increments the device's reference counter by 1 and return pointer
to the uio dma device//uio_dma_device_get
20.
F:Look up DMA mapping by area and direction.
/*
* Look up DMA mapping by area and direction.
* Must be called under device mutex.
*/
static struct uio_dma_mapping *uio_dma_mapping_lookup(
struct uio_dma_device *ud, struct uio_dma_area *area,
unsigned int dir)
21.
F: Create a new DMA mapping attached to device @ud.
/*
* Add new DMA mapping.
* Must be called under device mutex.
*/
static int uio_dma_mapping_add(struct uio_dma_device *ud, struct uio_dma_area *area,
unsigned int dir, struct uio_dma_mapping **map)
allocate a new uio_dma_mapping variable @m//kzalloc
Atomicly increase the uio dma area's reference counter //uio_dma_area_get
create stream DMA mapping for every chunks in @area, save the bus address in @m->dmaddr[i] //dma_map_single
Add the new uio_dma_mapping to the @ud's mapping list //list_add
save the uio dma mapping's pointer in *@map
22.
F: Delete DMA mapping indicated by @argp->mmap_offset which belongs to uio dma
device @argp->devid, if no one reference the uio dma device, drop
all mappings belong to it and free the device.
static int uio_dma_cmd_unmap(struct uio_dma_context *uc, void __user *argp)
copy content of typeof uio_dma_unmap_req variable from U space //copy_from_user
get the pointer to uio dma area indicated by @argp->mmap_offset //uio_dma_area_lookup
get the pointer to uio dma device indicated by @argp->devid //uio_dma_device_lookup
get lock of the uio dma device //uio_dma_device_lock
Look up DMA mapping by area and direction. //uio_dma_mapping_lookup
If find the corresponding DMA mapping,
Delete DMA mapping //uio_dma_mapping_del
free the lock of the uio dma device //uio_dma_device_unlock
decrease the reference counter of @ud, if no one using it, drop
all mappings belong to it and free @ud. //uio_dma_device_put
23.
F: Delete DMA mapping
/*
* Delete DMA mapping.
* Must be called under device mutex.
*/
static void uio_dma_mapping_del(struct uio_dma_device *ud, struct uio_dma_mapping *m)
delete DMA mapping @m //dma_unmap_single
delete @m from the @ud's mapping list //list_del
Decrease @area's ref. counter, if counter become 0, "Free" the area. //uio_dma_area_put
free the @m // kfree
24.
F: decrease the reference counter of @ud, if no one using it, drop
all mappings belong to it and free @ud.
static void uio_dma_device_put(struct uio_dma_device *ud)
decrease the uio dma device's reference counter //atomic_dec_and_test
if no one using it then Free the last reference to the UIO DMA device,
and Drops all mappings and releases @ud . //uio_dma_device_free
25.
F:
/*
* Free the last reference to the UIO DMA device.
* Drops all mappings and releases 'struct device'.
*/
static void uio_dma_device_free(struct uio_dma_device *ud)
Delete DMA mapping belongs to the @ud//__drop_dev_mappings
26.
F:Delete DMA mapping belongs to the @ud
static void __drop_dev_mappings(struct uio_dma_device *ud)
Delete DMA mapping belongs to the @ud //uio_dma_mapping_del
++++++++++++++ Interfaces
27.
F:
/**
* Open UIO DMA device (UIO driver interface).
* UIO driver calls this function to allocate new device id
* which can then be used by user space to create DMA mappings.
*/
int uio_dma_device_open(struct device *dev, uint32_t *id)
{
struct uio_dma_device *ud = kzalloc(sizeof(*ud), GFP_KERNEL);
if (!ud)
return -ENOMEM;
INIT_LIST_HEAD(&ud->mappings);
mutex_init(&ud->mutex);
atomic_set(&ud->refcount, 1);
ud->device = get_device(dev);//increment reference count for device.
if (!ud->device) {
kfree(ud);
return -ENODEV;
}
mutex_lock(&uio_dma_dev_mutex);
ud->id = uio_dma_dev_nextid++; // allocate the device id
list_add(&ud->list, &uio_dma_dev_list);//add the @ud to the global uio dma device
mutex_unlock(&uio_dma_dev_mutex);
*id = ud->id;// return the uio dma device id to the caller
UIO_DMA_DBG("added device. id %u %s\n", *id, dev_name(dev));
return 0;
}
EXPORT_SYMBOL(uio_dma_device_open);
28.
F:
/**
* Close UIO DMA device (UIO driver interface).
* UIO driver calls this function when the device is closed.
* All current mappings are destroyed.
*/
int uio_dma_device_close(uint32_t id)
{
struct uio_dma_device *ud;
// This can race with uio_dma_mapping_add(), which is perfectly save.
// Mappings will be cleaned up when uio_dma_mapping_add() releases
// the reference.
mutex_lock(&uio_dma_dev_mutex);
ud = __device_lookup(id);
if (!ud) {
UIO_DMA_DBG("removing bad device. id %u\n", id);
mutex_unlock(&uio_dma_dev_mutex);
return -ENOENT;
}
list_del(&ud->list);
uio_dma_device_put(ud);
mutex_unlock(&uio_dma_dev_mutex);
UIO_DMA_DBG("removed device. id %u %s\n", id, dev_name(ud->device));
uio_dma_device_put(ud);
return 0;
}
EXPORT_SYMBOL(uio_dma_device_close);
++++++++++++++
3. Important structures
/*
* DMA device.
* Holds a reference to 'struct device' and a list of DMA mappings
*/
struct uio_dma_device {
struct list_head list;
struct list_head mappings;
struct mutex mutex;
atomic_t refcount;
struct device *device;
uint32_t id;
};
struct uio_dma_unmap_req {
uint64_t mmap_offset;//used to find the uio dma area to free
uint32_t devid; // which uio dma device the uio dma area belongs to
uint32_t flags; //
uint8_t direction;
};
/*
* DMA mapping.
* Attached to a device.
* Holds a reference to an area.
*/
struct uio_dma_mapping {
struct list_head list;
struct uio_dma_area *area;
unsigned int direction;
uint64_t dmaddr[0];//Bus addresses of the DMA buffer
};
struct uio_dma_map_req {
uint64_t mmap_offset;
uint32_t flags;
uint32_t devid;
uint8_t direction;
uint32_t chunk_count;
uint32_t chunk_size;
uint64_t dmaddr[0];
};
struct uio_dma_alloc_req {
uint64_t dma_mask; //Indicate the device's address ability
uint16_t memnode;//?????? in uio_dma_area_alloc() calls alloc_pages_node()...
uint16_t cache;// cache mode
uint32_t flags;
uint32_t chunk_size;
uint32_t chunk_count;
uint64_t mmap_offset;// !!! This can be changed when overlap happens
// after calling of the append_to()
};
/*
* DMA area.
* Describes a chunk of DMAable memory.
* Attached to a DMA context.
*/
NOTE: You can only map the whole dma area in u space.
struct uio_dma_area {
struct list_head list;
atomic_t refcount;//[k:rw] ref counter of this structure
unsigned long mmap_offset;//*[k:rw] In kernel part find uio_dma_area by 'mmap_offset',
// then map the area to the userspace when userspace
// called mmap() within API uio_dma_alloc()
//* mmap_offset is in page unit.
unsigned long size;
unsigned int chunk_size;// bytes size of every chunk
unsigned int chunk_count;// quantity of the chunks
uint8_t cache;//[u:w] Indicates the area's cache property when mapped to the u space
void *addr[0];// beginning address of every chunk(virtual address of the process where the dma area mapped to is
// returned by mmap() )
//!!!! uio_dma_area_alloc(): should be kernel virtual address.
//As to the low memory the kernel virtual is just the kernel logical address
//there just a offset between kernel logical address and its physical address.
//----> the virtual addresses are used to create DMA mapping(uio_dma_mapping_add())
};
/*
* DMA context.
* Attached to a fd.
*/
struct uio_dma_context {
struct mutex mutex; //
struct list_head areas; // Used to arrange the uio_dma_area <????? The last allocated uio
dma area is placed at the beginning of the list>
};
/* List of active devices */
static struct list_head uio_dma_dev_list; //devices list allocated to usersapce
static struct mutex uio_dma_dev_mutex;
static uint32_t uio_dma_dev_nextid;
4. source code
uio-dma.h
uio-dma.c
ref:
Contents: uio-dma.c code review (V1.3)
Date: 11-02-2011
Last modified: 11-02-2011
-----------------------------------------------------------------------------------------------------------
Distributed and Embedded System Lab (分布式嵌入式系统实验室,兰州大学)
===============================================================
1. function list
2. function analysis
3. related structures
4. the source code
===============
1. function list
|- function
|| uio_dma_device_lock +
|| uio_dma_device_unlock +
|| __drop_dev_mappings +
|| uio_dma_device_free +
|| uio_dma_device_get +
|| uio_dma_device_put +
|| __device_lookup +
|| uio_dma_device_lookup +
|| uio_dma_device_open ======= ++++
|| uio_dma_device_close ====== ++++
|| __last_page +
|| uio_dma_area_free +
|| uio_dma_area_alloc +
|| uio_dma_area_get +
|| uio_dma_area_put +
|| uio_dma_area_lookup +
|| uio_dma_mapping_del +
|| uio_dma_mapping_add +
|| uio_dma_mapping_lookup +
|| uio_dma_context_lock +
|| uio_dma_context_unlock +
|| append_to +
|| uio_dma_cmd_alloc +
|| uio_dma_cmd_free +
|| uio_dma_cmd_map +
|| uio_dma_cmd_unmap +
|| uio_dma_ioctl +
|| __drop_ctx_areas +
|| uio_dma_close +
|| uio_dma_open +
|| uio_dma_poll +
|| uio_dma_read +
|| uio_dma_write +
|| uio_dma_vm_open +
|| uio_dma_vm_close +
|| uio_dma_vm_fault +
|| uio_dma_mmap +
|| uio_dma_init_module +
|| uio_dma_exit_module +
==============================
2. function analysis
1.
F: Register the misc device "uio-dma"
static int __init uio_dma_init_module(void)
Init device lists //INIT_LIST_HEAD
Init mutex used to protect uio_dma_dev_list//mutex_init
Register our misc device //misc_register[misc device is a special char dev]
2.
F: Unregister the misc device "uio-dma"
static void __exit uio_dma_exit_module(void)
//misc_deregister
3.
F: allocate a new uio-dma context and attached to the fd.
static int uio_dma_open(struct inode *inode, struct file * file)
/* Allocate new uio-dma context */ //kzalloc
Attach the uio-dma context with a fd.
4.
F: Try to free all the memory allocate under the context attached with
the fd which @inode belong to.
static int uio_dma_close(struct inode *inode, struct file *file)
As to the areas in context @uc, decrease their ref. counter.
If the counter become 0, "free" the areas. //__drop_ctx_areas
5.
F: As to the areas in context @uc, decrease their ref. counter.
If the counter become 0, "free" the areas.
static void __drop_ctx_areas(struct uio_dma_context *uc)
As to the areas in context @uc, decrease their ref. counter.
If the counter become 0, "free" the areas. //uio_dma_area_put
6.
F: Decrease @area's ref. counter, if counter become 0, "Free" the area.
static void uio_dma_area_put(struct uio_dma_area *area)
atomicly decrease @area's ref. counter.
if no one use it, then Release the area and related memory chunks.
//atomic_dec_and_test, uio_dma_area_free
7.
F:Release DMA area.
/*
* Release DMA area.
* Called only after all references to this area have been dropped.
*/
static void uio_dma_area_free(struct uio_dma_area *area)
Clear page Resered flag of all pages of all chunk belong to @area
and free the pages //__last_page, virt_to_page, ClearPageReserved
free the @area.
8.
F:get last page of the region dercribed by @addr and @size
static inline struct page *__last_page(void *addr, unsigned long size)
get last page of the region dercribed by @addr and @size//virt_to_page
9.
F:Fina a DMA area, map it to userspace.
static int uio_dma_mmap(struct file *file, struct vm_area_struct *vma)
Find a DMA area by offset(!We have allocate DMA area in uio_dma_ioctl()) //uio_dma_area_lookup
If this map is a partial mappings, reject it.// You can only map the whole dma area in u space.
Set the @vma's cache property indicated by struct uio_dma_area's cache member.
Create page table for every chunk in the dma area //page_to_pfn, virt_to_page, remap_pfn_range
Override the @vma's operations(do nothing here!) //vma->vm_ops = &uio_dma_mmap_ops;
NOTE:
In userspace:
mmap(NULL, da->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, da->mmap_offset);
In uio_dma_mmap():
unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
...
// Find an area that matches the offset and mmap it.
area = uio_dma_area_lookup(uc, offset);
So we can draw a conclusion:
Member of struct uio_dma_area 'mmap_offset' is in page unit.
10.
F:Look up DMA area by offset.
/*
* Look up DMA area by offset.
* Must be called under context mutex.
*/
static struct uio_dma_area *uio_dma_area_lookup(struct uio_dma_context *uc, uint64_t offset)
11.
F:Do differenct opration depends on the @cmd
(UIO_DMA_ALLOC, UIO_DMA_FREE, UIO_DMA_MAP, UIO_DMA_UNMAP)
static long uio_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Get uio_dma_context lock//uio_dma_context_lock
Do differenct opration depends on the @cmd // @cmd is passed from userspace ioctl()
Free uio_dma_context lock//uio_dma_context_unlock
12.
F:
static int uio_dma_cmd_alloc(struct uio_dma_context *uc, void __user *argp)
Copy the userspace's parameter(a uio_dma_alloc_req)//copy_from_user
Round up the chunk size to PAGE unit //roundup
Find list_head to append new area to //append_to
Allocate new DMA area, save every chunk's first page's kernel virtual address
in @area->addr[i] //uio_dma_area_alloc
/* Add to the context */
copy the @area's contents to userspace //copy_to_user
13.
F: return list_head to append new area to
/*
* Make sure new area (offset & size) does not everlap with
* the existing areas and return list_head to append new area to.
* Must be called under context mutex.
*/
static struct list_head *append_to(struct uio_dma_context *uc,
uint64_t *offset, unsigned int size)
14.
F:Allocate new DMA area, save every chunk's first page's kernel virtual address in @area->addr[i]
/*
* Allocate new DMA area.
*/
static struct uio_dma_area *uio_dma_area_alloc(uint64_t dma_mask, unsigned int memnode,
unsigned int cache, unsigned int chunk_size, unsigned int chunk_count)
allocate a uio_dma_area varible //kzalloc()
init the uio_dma_area varible
allocate pages for every chunk and save the (kernel virtual) address of
the first page in area->addr[i], set the last page's 'Reserved' flag//alloc_pages_node(),page_address()
return the uio_dma_area varible's pointer
15.
F: Delete dma area from @uc.area list and release the DMA area
static int uio_dma_cmd_free(struct uio_dma_context *uc, void __user *argp)
copy the uio_dma_free_req type variable from the u space//copy_from_user
Look up DMA area by offset //uio_dma_area_lookup
delete the dma area from the @uc.area list //list_del
Release DMA area // uio_dma_area_put
16.
F: Release DMA area
static void uio_dma_area_put(struct uio_dma_area *area)
Release DMA area //uio_dma_area_free, atomic_dec_and_test
17.
F: Create DMA mapping (it attaches uio dma area @argp.mmap_offset and uio dma device @argp.devid)
static int uio_dma_cmd_map(struct uio_dma_context *uc, void __user *argp)
copy the contents of the uio_dma_map_req variable from U space to @req //copy_from_user
find the uio dma area by @req.mmap_offset //uio_dma_area_lookup
Lookup UIO DMA device based by id, increments device refcount if found
and return pointer to it. //uio_dma_device_lookup
get the device's lock //uio_dma_device_lock
Look up DMA mapping by area and direction.//uio_dma_mapping_lookup
If the DMA mapping is not created before,
then, create a new DMA mapping attached to device @ud//uio_dma_mapping_add
//req.chunk_count = area->chunk_count; NOTE: the request dma map's chunk size and chunk_count may be changed
//req.chunk_size = area->chunk_size;
Copy the @req's contents to U space //copy_to_user
18.
F: Lookup UIO DMA device based by id, increments device refcount if found
and return pointer to it.
/*
* Lookup device by uio dma id.
* Caller must drop the reference to the returned
* device when it's done with it.
*/
static struct uio_dma_device* uio_dma_device_lookup(uint32_t id)
get lock uio_dma_dev_mutex //mutex_lock
Lookup UIO DMA device based by id, increments device refcount if found.//__device_lookup
free lock uio_dma_dev_mutex //mutex_unlock
19.
F:Lookup UIO DMA device based by id, increments device refcount if found.
/*
* Lookup UIO DMA device based by id.
* Must be called under uio_dma_dev_mutex.
* Increments device refcount if found.
*/
static struct uio_dma_device* __device_lookup(uint32_t id)
increments the device's reference counter by 1 and return pointer
to the uio dma device//uio_dma_device_get
20.
F:Look up DMA mapping by area and direction.
/*
* Look up DMA mapping by area and direction.
* Must be called under device mutex.
*/
static struct uio_dma_mapping *uio_dma_mapping_lookup(
struct uio_dma_device *ud, struct uio_dma_area *area,
unsigned int dir)
21.
F: Create a new DMA mapping attached to device @ud.
/*
* Add new DMA mapping.
* Must be called under device mutex.
*/
static int uio_dma_mapping_add(struct uio_dma_device *ud, struct uio_dma_area *area,
unsigned int dir, struct uio_dma_mapping **map)
allocate a new uio_dma_mapping variable @m//kzalloc
Atomicly increase the uio dma area's reference counter //uio_dma_area_get
create stream DMA mapping for every chunks in @area, save the bus address in @m->dmaddr[i] //dma_map_single
Add the new uio_dma_mapping to the @ud's mapping list //list_add
save the uio dma mapping's pointer in *@map
22.
F: Delete DMA mapping indicated by @argp->mmap_offset which belongs to uio dma
device @argp->devid, if no one reference the uio dma device, drop
all mappings belong to it and free the device.
static int uio_dma_cmd_unmap(struct uio_dma_context *uc, void __user *argp)
copy content of typeof uio_dma_unmap_req variable from U space //copy_from_user
get the pointer to uio dma area indicated by @argp->mmap_offset //uio_dma_area_lookup
get the pointer to uio dma device indicated by @argp->devid //uio_dma_device_lookup
get lock of the uio dma device //uio_dma_device_lock
Look up DMA mapping by area and direction. //uio_dma_mapping_lookup
If find the corresponding DMA mapping,
Delete DMA mapping //uio_dma_mapping_del
free the lock of the uio dma device //uio_dma_device_unlock
decrease the reference counter of @ud, if no one using it, drop
all mappings belong to it and free @ud. //uio_dma_device_put
23.
F: Delete DMA mapping
/*
* Delete DMA mapping.
* Must be called under device mutex.
*/
static void uio_dma_mapping_del(struct uio_dma_device *ud, struct uio_dma_mapping *m)
delete DMA mapping @m //dma_unmap_single
delete @m from the @ud's mapping list //list_del
Decrease @area's ref. counter, if counter become 0, "Free" the area. //uio_dma_area_put
free the @m // kfree
24.
F: decrease the reference counter of @ud, if no one using it, drop
all mappings belong to it and free @ud.
static void uio_dma_device_put(struct uio_dma_device *ud)
decrease the uio dma device's reference counter //atomic_dec_and_test
if no one using it then Free the last reference to the UIO DMA device,
and Drops all mappings and releases @ud . //uio_dma_device_free
25.
F:
/*
* Free the last reference to the UIO DMA device.
* Drops all mappings and releases 'struct device'.
*/
static void uio_dma_device_free(struct uio_dma_device *ud)
Delete DMA mapping belongs to the @ud//__drop_dev_mappings
26.
F:Delete DMA mapping belongs to the @ud
static void __drop_dev_mappings(struct uio_dma_device *ud)
Delete DMA mapping belongs to the @ud //uio_dma_mapping_del
++++++++++++++ Interfaces
27.
F:
/**
* Open UIO DMA device (UIO driver interface).
* UIO driver calls this function to allocate new device id
* which can then be used by user space to create DMA mappings.
*/
int uio_dma_device_open(struct device *dev, uint32_t *id)
{
struct uio_dma_device *ud = kzalloc(sizeof(*ud), GFP_KERNEL);
if (!ud)
return -ENOMEM;
INIT_LIST_HEAD(&ud->mappings);
mutex_init(&ud->mutex);
atomic_set(&ud->refcount, 1);
ud->device = get_device(dev);//increment reference count for device.
if (!ud->device) {
kfree(ud);
return -ENODEV;
}
mutex_lock(&uio_dma_dev_mutex);
ud->id = uio_dma_dev_nextid++; // allocate the device id
list_add(&ud->list, &uio_dma_dev_list);//add the @ud to the global uio dma device
mutex_unlock(&uio_dma_dev_mutex);
*id = ud->id;// return the uio dma device id to the caller
UIO_DMA_DBG("added device. id %u %s\n", *id, dev_name(dev));
return 0;
}
EXPORT_SYMBOL(uio_dma_device_open);
28.
F:
/**
* Close UIO DMA device (UIO driver interface).
* UIO driver calls this function when the device is closed.
* All current mappings are destroyed.
*/
int uio_dma_device_close(uint32_t id)
{
struct uio_dma_device *ud;
// This can race with uio_dma_mapping_add(), which is perfectly save.
// Mappings will be cleaned up when uio_dma_mapping_add() releases
// the reference.
mutex_lock(&uio_dma_dev_mutex);
ud = __device_lookup(id);
if (!ud) {
UIO_DMA_DBG("removing bad device. id %u\n", id);
mutex_unlock(&uio_dma_dev_mutex);
return -ENOENT;
}
list_del(&ud->list);
uio_dma_device_put(ud);
mutex_unlock(&uio_dma_dev_mutex);
UIO_DMA_DBG("removed device. id %u %s\n", id, dev_name(ud->device));
uio_dma_device_put(ud);
return 0;
}
EXPORT_SYMBOL(uio_dma_device_close);
++++++++++++++
3. Important structures
/*
* DMA device.
* Holds a reference to 'struct device' and a list of DMA mappings
*/
struct uio_dma_device {
struct list_head list;
struct list_head mappings;
struct mutex mutex;
atomic_t refcount;
struct device *device;
uint32_t id;
};
struct uio_dma_unmap_req {
uint64_t mmap_offset;//used to find the uio dma area to free
uint32_t devid; // which uio dma device the uio dma area belongs to
uint32_t flags; //
uint8_t direction;
};
/*
* DMA mapping.
* Attached to a device.
* Holds a reference to an area.
*/
struct uio_dma_mapping {
struct list_head list;
struct uio_dma_area *area;
unsigned int direction;
uint64_t dmaddr[0];//Bus addresses of the DMA buffer
};
struct uio_dma_map_req {
uint64_t mmap_offset;
uint32_t flags;
uint32_t devid;
uint8_t direction;
uint32_t chunk_count;
uint32_t chunk_size;
uint64_t dmaddr[0];
};
struct uio_dma_alloc_req {
uint64_t dma_mask; //Indicate the device's address ability
uint16_t memnode;//?????? in uio_dma_area_alloc() calls alloc_pages_node()...
uint16_t cache;// cache mode
uint32_t flags;
uint32_t chunk_size;
uint32_t chunk_count;
uint64_t mmap_offset;// !!! This can be changed when overlap happens
// after calling of the append_to()
};
/*
* DMA area.
* Describes a chunk of DMAable memory.
* Attached to a DMA context.
*/
NOTE: You can only map the whole dma area in u space.
struct uio_dma_area {
struct list_head list;
atomic_t refcount;//[k:rw] ref counter of this structure
unsigned long mmap_offset;//*[k:rw] In kernel part find uio_dma_area by 'mmap_offset',
// then map the area to the userspace when userspace
// called mmap() within API uio_dma_alloc()
//* mmap_offset is in page unit.
unsigned long size;
unsigned int chunk_size;// bytes size of every chunk
unsigned int chunk_count;// quantity of the chunks
uint8_t cache;//[u:w] Indicates the area's cache property when mapped to the u space
void *addr[0];// beginning address of every chunk(virtual address of the process where the dma area mapped to is
// returned by mmap() )
//!!!! uio_dma_area_alloc(): should be kernel virtual address.
//As to the low memory the kernel virtual is just the kernel logical address
//there just a offset between kernel logical address and its physical address.
//----> the virtual addresses are used to create DMA mapping(uio_dma_mapping_add())
};
/*
* DMA context.
* Attached to a fd.
*/
struct uio_dma_context {
struct mutex mutex; //
struct list_head areas; // Used to arrange the uio_dma_area <????? The last allocated uio
dma area is placed at the beginning of the list>
};
/* List of active devices */
static struct list_head uio_dma_dev_list; //devices list allocated to usersapce
static struct mutex uio_dma_dev_mutex;
static uint32_t uio_dma_dev_nextid;
4. source code
uio-dma.h
/* UIO-DMA kernel back-end Copyright (C) 2009 Qualcomm Inc. All rights reserved. Written by Max Krasnyansky <maxk@qualcommm.com> The UIO-DMA is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The UIO-DMA is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef UIO_DMA_H #define UIO_DMA_H #include <linux/types.h> #include <linux/device.h> /* kernel interfaces */ int uio_dma_device_open(struct device *dev, uint32_t *devid); int uio_dma_device_close(uint32_t devid); /* ioctl defines */ /* Caching modes */ enum { UIO_DMA_CACHE_DEFAULT = 0, UIO_DMA_CACHE_DISABLE, UIO_DMA_CACHE_WRITECOMBINE }; /* DMA mapping direction */ enum { UIO_DMA_BIDIRECTIONAL = 0, UIO_DMA_TODEVICE, UIO_DMA_FROMDEVICE }; #define UIO_DMA_ALLOC _IOW('U', 200, int) struct uio_dma_alloc_req { uint64_t dma_mask; uint16_t memnode; uint16_t cache; uint32_t flags; uint32_t chunk_size; uint32_t chunk_count; uint64_t mmap_offset; }; #define UIO_DMA_FREE _IOW('U', 201, int) struct uio_dma_free_req { uint64_t mmap_offset; }; #define UIO_DMA_MAP _IOW('U', 202, int) struct uio_dma_map_req { uint64_t mmap_offset; uint32_t flags; uint32_t devid; uint8_t direction; uint32_t chunk_count; uint32_t chunk_size; uint64_t dmaddr[0]; }; #define UIO_DMA_UNMAP _IOW('U', 203, int) struct uio_dma_unmap_req { uint64_t mmap_offset; uint32_t devid; uint32_t flags; uint8_t direction; }; #endif /* UIO_DMA_H */
uio-dma.c
/* UIO-DMA kernel backend Copyright (C) 2009 Qualcomm Inc. All rights reserved. Written by Max Krasnyansky <maxk@qualcommm.com> The UIO-DMA is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The UIO-DMA is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/poll.h> #include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/sysctl.h> #include <linux/wait.h> #include <linux/miscdevice.h> #include <linux/ioport.h> #include <linux/pci.h> #include <linux/file.h> #include <asm/io.h> #include "uio-dma.h" #ifdef DBG #define UIO_DMA_DBG(args...) printk(KERN_DEBUG "uio-dma: " args) #else #define UIO_DMA_DBG(args...) #endif #define UIO_DMA_INFO(args...) printk(KERN_INFO "uio-dma: " args) #define UIO_DMA_ERR(args...) printk(KERN_ERR "uio-dma: " args) #define VERSION "2.0" char uio_dma_driver_name[] = "uio-dma"; char uio_dma_driver_string[] = "UIO DMA kernel backend"; char uio_dma_driver_version[] = VERSION; char uio_dma_copyright[] = "Copyright (c) 2009 Qualcomm Inc. Written by Max Krasnyansky <maxk@qualcomm.com>"; /* List of active devices */ static struct list_head uio_dma_dev_list; static struct mutex uio_dma_dev_mutex; static uint32_t uio_dma_dev_nextid; /* * DMA device. * Holds a reference to 'struct device' and a list of DMA mappings */ struct uio_dma_device { struct list_head list; struct list_head mappings; struct mutex mutex; atomic_t refcount; struct device *device; uint32_t id; }; /* * DMA area. * Describes a chunk of DMAable memory. * Attached to a DMA context. */ struct uio_dma_area { struct list_head list; atomic_t refcount; unsigned long mmap_offset; unsigned long size; unsigned int chunk_size; unsigned int chunk_count; uint8_t cache; void *addr[0]; }; /* * DMA mapping. * Attached to a device. * Holds a reference to an area. */ struct uio_dma_mapping { struct list_head list; struct uio_dma_area *area; unsigned int direction; uint64_t dmaddr[0]; }; /* * DMA context. * Attached to a fd. */ struct uio_dma_context { struct mutex mutex; struct list_head areas; }; static void uio_dma_mapping_del(struct uio_dma_device *ud, struct uio_dma_mapping *m); /* ---- Devices ---- */ static void uio_dma_device_lock(struct uio_dma_device *ud) { mutex_lock(&ud->mutex); } static void uio_dma_device_unlock(struct uio_dma_device *ud) { mutex_unlock(&ud->mutex); } /* * Drop all mappings on this device */ static void __drop_dev_mappings(struct uio_dma_device *ud) { struct uio_dma_mapping *m, *n; list_for_each_entry_safe(m, n, &ud->mappings, list) uio_dma_mapping_del(ud, m); } /* * Free the last reference to the UIO DMA device. * Drops all mappings and releases 'struct device'. */ static void uio_dma_device_free(struct uio_dma_device *ud) { __drop_dev_mappings(ud); UIO_DMA_DBG("freed device. %s\n", dev_name(ud->device)); put_device(ud->device); kfree(ud); } static struct uio_dma_device *uio_dma_device_get(struct uio_dma_device *ud) { atomic_inc(&ud->refcount); return ud; } static void uio_dma_device_put(struct uio_dma_device *ud) { if (atomic_dec_and_test(&ud->refcount)) uio_dma_device_free(ud); } /* * Lookup UIO DMA device based by id. * Must be called under uio_dma_dev_mutex. * Increments device refcount if found. */ static struct uio_dma_device* __device_lookup(uint32_t id) { struct uio_dma_device *ud; list_for_each_entry(ud, &uio_dma_dev_list, list) { if (ud->id == id) return uio_dma_device_get(ud); } return NULL; } /* * Lookup device by uio dma id. * Caller must drop the reference to the returned * device when it's done with it. */ static struct uio_dma_device* uio_dma_device_lookup(uint32_t id) { struct uio_dma_device *ud; mutex_lock(&uio_dma_dev_mutex); ud = __device_lookup(id); mutex_unlock(&uio_dma_dev_mutex); return ud; } /** * Open UIO DMA device (UIO driver interface). * UIO driver calls this function to allocate new device id * which can then be used by user space to create DMA mappings. */ int uio_dma_device_open(struct device *dev, uint32_t *id) { struct uio_dma_device *ud = kzalloc(sizeof(*ud), GFP_KERNEL); if (!ud) return -ENOMEM; INIT_LIST_HEAD(&ud->mappings); mutex_init(&ud->mutex); atomic_set(&ud->refcount, 1); ud->device = get_device(dev); if (!ud->device) { kfree(ud); return -ENODEV; } mutex_lock(&uio_dma_dev_mutex); ud->id = uio_dma_dev_nextid++; list_add(&ud->list, &uio_dma_dev_list); mutex_unlock(&uio_dma_dev_mutex); *id = ud->id; UIO_DMA_DBG("added device. id %u %s\n", *id, dev_name(dev)); return 0; } EXPORT_SYMBOL(uio_dma_device_open); /** * Close UIO DMA device (UIO driver interface). * UIO driver calls this function when the device is closed. * All current mappings are destroyed. */ int uio_dma_device_close(uint32_t id) { struct uio_dma_device *ud; // This can race with uio_dma_mapping_add(), which is perfectly save. // Mappings will be cleaned up when uio_dma_mapping_add() releases // the reference. mutex_lock(&uio_dma_dev_mutex); ud = __device_lookup(id); if (!ud) { UIO_DMA_DBG("removing bad device. id %u\n", id); mutex_unlock(&uio_dma_dev_mutex); return -ENOENT; } list_del(&ud->list); uio_dma_device_put(ud); mutex_unlock(&uio_dma_dev_mutex); UIO_DMA_DBG("removed device. id %u %s\n", id, dev_name(ud->device)); uio_dma_device_put(ud); return 0; } EXPORT_SYMBOL(uio_dma_device_close); /* ---- Areas ---- */ static inline struct page *__last_page(void *addr, unsigned long size) { return virt_to_page(addr + (PAGE_SIZE << get_order(size)) - 1); } /* * Release DMA area. * Called only after all references to this area have been dropped. */ static void uio_dma_area_free(struct uio_dma_area *area) { struct page *page, *last; int i; UIO_DMA_DBG("area free. %p mmap_offset %lu\n", area, area->mmap_offset); for (i=0; i < area->chunk_count; i++) { last = __last_page(area->addr[i], area->chunk_size); for (page = virt_to_page(area->addr[i]); page <= last; page++) ClearPageReserved(page); free_pages((unsigned long) area->addr[i], get_order(area->chunk_size)); } kfree(area); } /* * Allocate new DMA area. */ static struct uio_dma_area *uio_dma_area_alloc(uint64_t dma_mask, unsigned int memnode, unsigned int cache, unsigned int chunk_size, unsigned int chunk_count) { struct uio_dma_area *area; struct page *page, *last; int i, gfp; area = kzalloc(sizeof(*area) + sizeof(void *) * chunk_count, GFP_KERNEL); if (!area) return NULL; UIO_DMA_DBG("area alloc. area %p chunk_size %u chunk_count %u\n", area, chunk_size, chunk_count); gfp = GFP_KERNEL | __GFP_NOWARN; if (dma_mask < DMA_64BIT_MASK) { if (dma_mask < DMA_32BIT_MASK) gfp |= GFP_DMA; else gfp |= GFP_DMA32; } atomic_set(&area->refcount, 1); area->chunk_size = chunk_size; area->chunk_count = chunk_count; area->size = chunk_size * chunk_count; area->cache = cache; for (i=0; i < chunk_count; i++) { page = alloc_pages_node(memnode, gfp, get_order(chunk_size)); if (!page) { area->chunk_count = i; uio_dma_area_free(area); return NULL; } area->addr[i] = page_address(page); last = __last_page(area->addr[i], chunk_size); for (; page <= last; page++) SetPageReserved(page); } return area; } static struct uio_dma_area *uio_dma_area_get(struct uio_dma_area *area) { atomic_inc(&area->refcount); return area; } static void uio_dma_area_put(struct uio_dma_area *area) { if (atomic_dec_and_test(&area->refcount)) uio_dma_area_free(area); } /* * Look up DMA area by offset. * Must be called under context mutex. */ static struct uio_dma_area *uio_dma_area_lookup(struct uio_dma_context *uc, uint64_t offset) { struct uio_dma_area *area; UIO_DMA_DBG("area lookup. context %p offset %llu\n", uc, (unsigned long long) offset); list_for_each_entry(area, &uc->areas, list) { if (area->mmap_offset == offset) return area; } return NULL; } /* ---- Mappings ---- */ /* * Delete DMA mapping. * Must be called under device mutex. */ static void uio_dma_mapping_del(struct uio_dma_device *ud, struct uio_dma_mapping *m) { unsigned int i; UIO_DMA_DBG("mapping del. device %s mapping %p area %p\n", dev_name(ud->device), m, m->area); for (i=0; i < m->area->chunk_count; i++) dma_unmap_single(ud->device, m->dmaddr[i], m->area->chunk_size, m->direction); list_del(&m->list); uio_dma_area_put(m->area); kfree(m); } /* * Add new DMA mapping. * Must be called under device mutex. */ static int uio_dma_mapping_add(struct uio_dma_device *ud, struct uio_dma_area *area, unsigned int dir, struct uio_dma_mapping **map) { struct uio_dma_mapping *m; int i, n, err; m = kzalloc(sizeof(*m) + sizeof(dma_addr_t) * area->chunk_count, GFP_KERNEL); if (!m) return -ENOMEM; UIO_DMA_DBG("maping add. device %s area %p chunk_size %u chunk_count %u\n", dev_name(ud->device), area, area->chunk_size, area->chunk_count); m->area = uio_dma_area_get(area); m->direction = dir; for (i=0; i < area->chunk_count; i++) { m->dmaddr[i] = dma_map_single(ud->device, area->addr[i], area->chunk_size, dir); if (!m->dmaddr[i]) { err = -EBADSLT; goto failed; } UIO_DMA_DBG("maped. device %s area %p chunk #%u dmaddr %llx\n", dev_name(ud->device), area, i, (unsigned long long) m->dmaddr[i]); } list_add(&m->list, &ud->mappings); *map = m; return 0; failed: for (n = 0; n < i; n++) dma_unmap_single(ud->device, m->dmaddr , m->area->chunk_size, dir); uio_dma_area_put(m->area); kfree(m); return err; } /* * Look up DMA mapping by area and direction. * Must be called under device mutex. */ static struct uio_dma_mapping *uio_dma_mapping_lookup( struct uio_dma_device *ud, struct uio_dma_area *area, unsigned int dir) { struct uio_dma_mapping *m; UIO_DMA_DBG("mapping lookup. device %s area %p dir %u\n", dev_name(ud->device), area, dir); list_for_each_entry(m, &ud->mappings, list) { if (m->area == area && m->direction == dir) return m; } return NULL; } /* ---- Context ---- */ static void uio_dma_context_lock(struct uio_dma_context *uc) { mutex_lock(&uc->mutex); } static void uio_dma_context_unlock(struct uio_dma_context *uc) { mutex_unlock(&uc->mutex); } /* ---- User interface ---- */ /* * Make sure new area (offset & size) does not everlap with * the existing areas and return list_head to append new area to. * Must be called under context mutex. */ static struct list_head *append_to(struct uio_dma_context *uc, uint64_t *offset, unsigned int size) { unsigned long start, end, astart, aend; struct uio_dma_area *area; struct list_head *last; start = *offset; end = start + size; UIO_DMA_DBG("adding area. context %p start %lu end %lu\n", uc, start, end); last = &uc->areas; list_for_each_entry(area, &uc->areas, list) { astart = area->mmap_offset; aend = astart + area->size; UIO_DMA_DBG("checking area. context %p start %lu end %lu\n", uc, astart, aend); /* Since the list is sorted we know at this point that * new area goes before this one. */ if (end <= astart) break; last = &area->list; if ((start >= astart && start < aend) || (end > astart && end <= aend)) { /* Found overlap. Set start to the end of the current * area and keep looking. */ start = aend; end = start + size; continue; } } *offset = start; return last; } static int uio_dma_cmd_alloc(struct uio_dma_context *uc, void __user *argp) { struct uio_dma_alloc_req req; struct uio_dma_area *area; struct list_head *where; unsigned long size; if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; if (!req.chunk_size || !req.chunk_count) return -EINVAL; req.chunk_size = roundup(req.chunk_size, PAGE_SIZE); size = req.chunk_size * req.chunk_count; UIO_DMA_DBG("alloc req enter. context %p offset %llu chunk_size %u chunk_count %u (total %lu)\n", uc, (unsigned long long) req.mmap_offset, req.chunk_size, req.chunk_count, size); where = append_to(uc, &req.mmap_offset, size); if (!where) return -EBUSY; area = uio_dma_area_alloc(req.dma_mask, req.memnode, req.cache, req.chunk_size, req.chunk_count); if (!area) return -ENOMEM; /* Add to the context */ area->mmap_offset = req.mmap_offset; list_add(&area->list, where); if (copy_to_user(argp, &req, sizeof(req))) { list_del(&area->list); uio_dma_area_put(area); return EFAULT; } UIO_DMA_DBG("alloc req exit. context %p offset %llu size %lu mask %llx node %u\n", uc, (unsigned long long) area->mmap_offset, area->size, (unsigned long long) req.dma_mask, req.memnode); return 0; } static int uio_dma_cmd_free(struct uio_dma_context *uc, void __user *argp) { struct uio_dma_free_req req; struct uio_dma_area *area; if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; UIO_DMA_DBG("free req. context %p offset %llu\n", uc, req.mmap_offset); area = uio_dma_area_lookup(uc, req.mmap_offset); if (!area) return -ENOENT; list_del(&area->list); uio_dma_area_put(area); return 0; } static int uio_dma_cmd_map(struct uio_dma_context *uc, void __user *argp) { struct uio_dma_map_req req; struct uio_dma_mapping *m; struct uio_dma_area *area; struct uio_dma_device *ud; int err; if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; UIO_DMA_DBG("map req. context %p offset %llu devid %u\n", uc, req.mmap_offset, req.devid); area = uio_dma_area_lookup(uc, req.mmap_offset); if (!area) return -ENOENT; if (req.chunk_count < area->chunk_count) return -EINVAL; ud = uio_dma_device_lookup(req.devid); if (!ud) return -ENODEV; uio_dma_device_lock(ud); m = uio_dma_mapping_lookup(ud, area, req.direction); if (m) { err = -EALREADY; goto out; } err = uio_dma_mapping_add(ud, area, req.direction, &m); if (err) goto out; req.chunk_count = area->chunk_count; req.chunk_size = area->chunk_size; if (copy_to_user(argp, &req, sizeof(req))) goto fault; /* Copy dma addresses */ if (copy_to_user(argp + sizeof(req), m->dmaddr, sizeof(uint64_t) * area->chunk_count)) goto fault; err = 0; goto out; fault: err = EFAULT; uio_dma_mapping_del(ud, m); out: uio_dma_device_unlock(ud); uio_dma_device_put(ud); return err; } static int uio_dma_cmd_unmap(struct uio_dma_context *uc, void __user *argp) { struct uio_dma_unmap_req req; struct uio_dma_area *area; struct uio_dma_mapping *m; struct uio_dma_device *ud; int err; if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; UIO_DMA_DBG("map req. context %p offset %llu devid %u\n", uc, req.mmap_offset, req.devid); area = uio_dma_area_lookup(uc, req.mmap_offset); if (!area) return -ENOENT; ud = uio_dma_device_lookup(req.devid); if (!ud) return -ENODEV; uio_dma_device_lock(ud); err = -ENOENT; m = uio_dma_mapping_lookup(ud, area, req.direction); if (m) { uio_dma_mapping_del(ud, m); err = 0; } uio_dma_device_unlock(ud); uio_dma_device_put(ud); return err; } static long uio_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct uio_dma_context *uc = file->private_data; void __user * argp = (void __user *) arg; int err; UIO_DMA_DBG("ioctl. context %p cmd %d arg %lu\n", uc, cmd, arg); if (!uc) return -EBADFD; uio_dma_context_lock(uc); switch (cmd) { case UIO_DMA_ALLOC: err = uio_dma_cmd_alloc(uc, argp); break; case UIO_DMA_MAP: err = uio_dma_cmd_map(uc, argp); break; case UIO_DMA_UNMAP: err = uio_dma_cmd_unmap(uc, argp); break; case UIO_DMA_FREE: err = uio_dma_cmd_free(uc, argp); break; default: err = -EINVAL; break; }; uio_dma_context_unlock(uc); return err; } static void __drop_ctx_areas(struct uio_dma_context *uc) { struct uio_dma_area *area, *n; list_for_each_entry_safe(area, n, &uc->areas, list) uio_dma_area_put(area); } static int uio_dma_close(struct inode *inode, struct file *file) { struct uio_dma_context *uc = file->private_data; if (!uc) return 0; UIO_DMA_DBG("closed context %p\n", uc); __drop_ctx_areas(uc); file->private_data = NULL; kfree(uc); return 0; } static int uio_dma_open(struct inode *inode, struct file * file) { struct uio_dma_context *uc; /* Allocate new context */ uc = kzalloc(sizeof(*uc), GFP_KERNEL); if (!uc) return -ENOMEM; mutex_init(&uc->mutex); INIT_LIST_HEAD(&uc->areas); file->private_data = uc; UIO_DMA_DBG("created context %p\n", uc); return 0; } static unsigned int uio_dma_poll(struct file *file, poll_table *wait) { return -ENOSYS; } static ssize_t uio_dma_read(struct file * file, char __user * buf, size_t count, loff_t *pos) { return -ENOSYS; } static ssize_t uio_dma_write(struct file * file, const char __user * buf, size_t count, loff_t *pos) { return -ENOSYS; } static void uio_dma_vm_open(struct vm_area_struct *vma) { } static void uio_dma_vm_close(struct vm_area_struct *vma) { } static int uio_dma_vm_fault(struct vm_area_struct *area, struct vm_fault *fdata) { return VM_FAULT_SIGBUS; } static struct vm_operations_struct uio_dma_mmap_ops = { .open = uio_dma_vm_open, .close = uio_dma_vm_close, .fault = uio_dma_vm_fault }; static int uio_dma_mmap(struct file *file, struct vm_area_struct *vma) { struct uio_dma_context *uc = file->private_data; struct uio_dma_area *area; unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; unsigned long offset = vma->vm_pgoff * PAGE_SIZE; unsigned long pfn; int i; if (!uc) return -EBADFD; UIO_DMA_DBG("mmap. context %p start %lu size %lu offset %lu\n", uc, start, size, offset); // Find an area that matches the offset and mmap it. area = uio_dma_area_lookup(uc, offset); if (!area) return -ENOENT; // We do not do partial mappings, sorry if (area->size != size) return -EOVERFLOW; switch (area->cache) { case UIO_DMA_CACHE_DISABLE: vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); break; case UIO_DMA_CACHE_WRITECOMBINE: #ifdef pgprot_writecombine vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); #endif break; default: /* Leave as is */ break; } for (i=0; i < area->chunk_count; i++) { pfn = page_to_pfn(virt_to_page(area->addr[i])); if (remap_pfn_range(vma, start, pfn, area->chunk_size, vma->vm_page_prot)) return -EIO; start += area->chunk_size; } vma->vm_ops = &uio_dma_mmap_ops; return 0; } static struct file_operations uio_dma_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = uio_dma_read, .write = uio_dma_write, .poll = uio_dma_poll, .open = uio_dma_open, .release = uio_dma_close, .mmap = uio_dma_mmap, .unlocked_ioctl = uio_dma_ioctl, }; static struct miscdevice uio_dma_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "uio-dma", .fops = &uio_dma_fops, }; static int __init uio_dma_init_module(void) { int err; INIT_LIST_HEAD(&uio_dma_dev_list); mutex_init(&uio_dma_dev_mutex); printk(KERN_INFO "%s - version %s\n", uio_dma_driver_string, uio_dma_driver_version); printk(KERN_INFO "%s\n", uio_dma_copyright); err = misc_register(&uio_dma_miscdev); if (err) { UIO_DMA_ERR("failed to register misc device\n"); return err; } return err; } static void __exit uio_dma_exit_module(void) { misc_deregister(&uio_dma_miscdev); } module_init(uio_dma_init_module); module_exit(uio_dma_exit_module); /* ---- */ MODULE_AUTHOR("Max Krasnyansky <maxk@qualcomm.com>"); MODULE_DESCRIPTION("uio-dma kernel backend"); MODULE_LICENSE("GPL"); MODULE_VERSION(VERSION);
ref:
相关文章推荐
- Educational Codeforces Round 15 E. Analysis of Pathes in Functio(倍增)★ ★
- video_device ops in user_space
- UIO (userspace I/O) 简介
- Checkpoint/Restore in Userspace(CRIU)的安装与使用(CentOS 7.2)
- FUSE(Filesystem in Userspace)简介和使用
- There is already a statement named SysUser.getUserByOrganCode in this SqlMap. spring id相同
- FUSE(Filesystem in userspace)(用户空间文件系统),user-space框架简单介绍
- FUSE - implementing filesystems in user space
- Kernel space DMA and User space DMA
- Doing It in User Space
- 编译选项导致死机(Unaligned userspace access in "XXX.exe" )
- Educational Codeforces Round 15 E - Analysis of Pathes in Functional Graph
- There is already a statement named SysUser.getUserByOrganCode in this SqlMap. spring id相同
- Writing a Real Driver -- In User Space
- Educational Codeforces Round 15 Analysis of Pathes in Functional Graph rmq 变形
- Educational Codeforces Round 15 E Analysis of Pathes in Functional Graph(倍增)
- Linux Filesystem in Userspace(FUSE)
- how to compile source code of "Data Structures & Algorithm Analysis in Java" writen by Mark Allen Weiss
- Educational Codeforces Round 15 E. Analysis of Pathes in Functional Graph (倍增RMQ)
- Linux Filesystem in Userspace(FUSE)