on-chip coherent memory API for DMA
The purpose of this API is to find a way of declaring on-chip memory as a pool for dma_alloc_coherent() that can be useful to all architectures.
The proposed API is:
nt dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, dma_addr_t device_addr, size_t size, int flags)
This API basically declares a region of memory to be handed out by dma_alloc_coherent when it's asked for coherent memory for this device.
bus_addr is the physical address to which the memory is currently assigned in the bus responding region
device_addr is the physical address the device needs to be programmed with actually to address this memory.
size is the size of the area (must be multiples of PAGE_SIZE).
The flags is where all the magic is. They can be or'd together and are
DMA_MEMORY_MAP - request that the memory returned from dma_alloc_coherent() be directly writeable.
DMA_MEMORY_IO - request that the memory returned from dma_alloc_coherent() be addressable using read/write/memcpy_toio etc.
One or both of these flags must be present
DMA_MEMORY_INCLUDES_CHILDREN - make the declared memory be allocated by dma_alloc_coherent of any child devices of this one (for memory residing on a bridge).
DMA_MEMORY_EXCLUSIVE - only allocate memory from the declared regions. Do not allow dma_alloc_coherent() to fall back to system memory when it's out of memory in the declared region.
The return value would be either DMA_MEMORY_MAP or DMA_MEMORY_IO and must correspond to a passed in flag (i.e. no returning DMA_MEMORY_IO if only DMA_MEMORY_MAP were passed in) or zero for failure.
I think also, it's reasonable only to have a single declared region per device.
Implementation details
Obviously, the big change is that dma_alloc_coherent() may now be handing out memory that can't be directly written to (in the case of a DMA_MEMORY_IO return).
I envisage implementing an internal per device resource allocator in drivers/base into which each platform allocator can plug do do the heavy allocation lifting (they'd still get to do the platform magic to make the returned region visible as memory if necessary).
The API would be platform optional, with platforms not wishing to implement it simply hard coding a return 0.
There would also be a corresponding
void dma_release_declared_memory(struct device *dev)
whose job would be to clean up unconditionally (and not check if the memory were still in use).
I already have this coded up on x86 and implemented for a SCSI card I have with four channels and a shared 2MB memory area on chip. I'll post the implementations when I get it cleaned up.
James
Quoted 59 lines · James Bottomley
The purpose of this API is to find a way of declaring on-chip memory as a pool for
dma_alloc_coherent()that can be useful to all architectures.The proposed API is:
nt
dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, dma_addr_t device_addr, size_t size, int flags)This API basically declares a region of memory to be handed out by
dma_alloc_coherentwhen it's asked for coherent memory for this device.
bus_addris the physical address to which the memory is currently assigned in the bus responding region
device_addris the physical address the device needs to be programmed with actually to address this memory.size is the size of the area (must be multiples of
PAGE_SIZE).The flags is where all the magic is. They can be or'd together and are
DMA_MEMORY_MAP- request that the memory returned fromdma_alloc_coherent()be directly writeable.
DMA_MEMORY_IO- request that the memory returned fromdma_alloc_coherent()be addressable using read/write/memcpy_toio etc.One or both of these flags must be present
DMA_MEMORY_INCLUDES_CHILDREN- make the declared memory be allocated bydma_alloc_coherentof any child devices of this one (for memory residing on a bridge).
DMA_MEMORY_EXCLUSIVE- only allocate memory from the declared regions. Do not allowdma_alloc_coherent()to fall back to system memory when it's out of memory in the declared region.The return value would be either
DMA_MEMORY_MAPorDMA_MEMORY_IOand must correspond to a passed in flag (i.e. no returningDMA_MEMORY_IOif onlyDMA_MEMORY_MAPwere passed in) or zero for failure.I think also, it's reasonable only to have a single declared region per device.
Implementation details
Obviously, the big change is that
dma_alloc_coherent()may now be handing out memory that can't be directly written to (in the case of aDMA_MEMORY_IOreturn).I envisage implementing an internal per device resource allocator in
drivers/baseinto which each platform allocator can plug do do the heavy allocation lifting (they'd still get to do the platform magic to make the returned region visible as memory if necessary).The API would be platform optional, with platforms not wishing to implement it simply hard coding a return 0.
There would also be a corresponding
Quoted 11 lines
void
dma_release_declared_memory(struct device *dev)whose job would be to clean up unconditionally (and not check if the memory were still in use).
I already have this coded up on x86 and implemented for a SCSI card I have with four channels and a shared 2MB memory area on chip. I'll post the implementations when I get it cleaned up.
The proposed API looks like it will solve our problem. I will work with Ian to test this with our drivers.
Jamey
Quoted 6 lines · James Bottomley
dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, dma_addr_t device_addr, size_t size, int flags)...
Quoted 7 lines
The flags is where all the magic is. They can be or'd together and are
DMA_MEMORY_MAP- request that the memory returned fromdma_alloc_coherent()be directly writeable.
DMA_MEMORY_IO- request that the memory returned fromdma_alloc_coherent()be addressable using read/write/memcpy_toio etc.
The API looked OK except this part didn't make sense to me, since as I understand things dma_alloc_coherent() is guaranteed to have the DMA_MEMORY_MAP semantics at all times ... the CPU virtual address returned may always be directly written. That's certainly how all the code I've seen using dma_alloc_coherent() works.
It'd make more sense if the routine were "dma_declare_memory()", and DMA_MEMORY_MAP meant it was OK to return from dma_alloc_coherent(), while DMA_MEMORY_IO meant the dma_alloc_coherent() would always fail.
If I understand what you're trying to do, DMA_MEMORY_IO supports a new kind of DMA memory, and is necessary to work on those IBM boxes you were talking about ... where dma_alloc_coherent() can't work, and the "indirectly accessible" memory would need to be allocated using some new alloc/free API. Or were you maybe trying to get at that "can be mmapped to userspace" distinction?
Also in terms of implementation, I noticed that if there's a dev->dma_mem, the GFP_* flags are ignored. For __GFP_NOFAIL that seems buglike, but not critical. (Just looked at x86.) Might be worth just passing the flags down so that behavior can be upgraded later.
- Dave
Quoted 9 lines · David Brownell
The API looked OK except this part didn't make sense to me, since as I understand things
dma_alloc_coherent()is guaranteed to have theDMA_MEMORY_MAPsemantics at all times ... the CPU virtual address returned may always be directly written. That's certainly how all the code I've seen usingdma_alloc_coherent()works.It'd make more sense if the routine were "
dma_declare_memory()", andDMA_MEMORY_MAPmeant it was OK to return fromdma_alloc_coherent(), whileDMA_MEMORY_IOmeant thedma_alloc_coherent()would always fail.
You need an allocator paired with IO memory. If the driver allows for DMA_MEMORY_IO then it's not unreasonable to expect it to have such memory returned by dma_alloc_coherent() rather than adding yet another allocator API.
Quoted 6 lines
If I understand what you're trying to do,
DMA_MEMORY_IOsupports a new kind of DMA memory, and is necessary to work on those IBM boxes you were talking about ... wheredma_alloc_coherent()can't work, and the "indirectly accessible" memory would need to be allocated using some new alloc/free API. Or were you maybe trying to get at that "can be mmapped to userspace" distinction?
No, this IO vs MAP thing is can be mapped directly to *kernel* space. i.e. whether the driver can use it via ordinary dereferencing or has to use the I/O memory accessor functions.
Quoted 5 lines
Also in terms of implementation, I noticed that if there's a
dev->dma_mem, the GFP_* flags are ignored. For__GFP_NOFAILthat seems buglike, but not critical. (Just looked at x86.) Might be worth just passing the flags down so that behavior can be upgraded later.
Actually, there's no point respecting the flags for the on chip region. Either the memory is there or it isn't. If it isn't there, then you either fall through to the ordinary allocator (where the flags are respected) or fail if the DMA_MEMORY_EXCLUSIVE flag was specified.
James
Quoted 14 lines · James Bottomley
Quoted 10 lines · David Brownell
The API looked OK except this part didn't make sense to me, since as I understand things
dma_alloc_coherent()is guaranteed to have theDMA_MEMORY_MAPsemantics at all times ... the CPU virtual address returned may always be directly written. That's certainly how all the code I've seen usingdma_alloc_coherent()works.It'd make more sense if the routine were "
dma_declare_memory()", andDMA_MEMORY_MAPmeant it was OK to return fromdma_alloc_coherent(), whileDMA_MEMORY_IOmeant thedma_alloc_coherent()would always fail.
Quoted 4 lines
You need an allocator paired with IO memory. If the driver allows for
DMA_MEMORY_IOthen it's not unreasonable to expect it to have such memory returned bydma_alloc_coherent()rather than adding yet another allocator API.
Seems unreasonable to me, unless there's also an API to change the mode of the dma_alloc_coherent() memory from the normal "CPU can read/write as usual" to the exotic "need to use special memory accessors". (And another to report what mode the API is in at the current moment.)
And I don't like modal APIs like that, which is why it'd make more sense to me to have a new allocator API for this new kind of DMA memory. (Which IS for that IBM processor, yes?)
Alternatively, modify dma_alloc_coherent() to say what kind of address it must return. Since this is a "generic" DMA API, the caller of dma_alloc_coherent() shouldn't need to be guessing how they may actually use the memory returned. That new "must guess" requirement will break some code...
Quoted 10 lines
Quoted 5 lines
Also in terms of implementation, I noticed that if there's a
dev->dma_mem, the GFP_* flags are ignored. For__GFP_NOFAILthat seems buglike, but not critical. (Just looked at x86.) Might be worth just passing the flags down so that behavior can be upgraded later.Actually, there's no point respecting the flags for the on chip region. Either the memory is there or it isn't. If it isn't there, then you either fall through to the ordinary allocator (where the flags are
respected) or fail if theDMA_MEMORY_EXCLUSIVEflag was specified.
So -- you're saying it's not a bug that a __GFP_NOFAIL|__GFP_WAIT allocation be able to fail? Curious. I'd have thought the API was clear about that. Allocating 128 MB from a 1 MB region must of course fail, but allocating one page just needs a wait/wakeup.
- Dave
Quoted 5 lines · David Brownell
Seems unreasonable to me, unless there's also an API to change the mode of the
dma_alloc_coherent()memory from the normal "CPU can read/write as usual" to the exotic "need to use special memory accessors". (And another to report what mode the API is in at the current moment.)
No. That's why you specify how you'd like the memory to be treated. If you don't want the memory to be accessible only via IO accessors, then you specify DMA_MEMORY_MAP and take the failure if the platform can't handle it.
Quoted 3 lines
And I don't like modal APIs like that, which is why it'd make more sense to me to have a new allocator API for this new kind of DMA memory. (Which IS for that IBM processor, yes?)
There is no "new" kind of memory. This is currently how *all* I/O memory is accessed. DMA_MEMORY_MAP is actually the new bit since it allows I/O memory to be treated as normal memory.
Quoted 5 lines
Alternatively, modify
dma_alloc_coherent()to say what kind of address it must return. Since this is a "generic" DMA API, the caller ofdma_alloc_coherent()shouldn't need to be guessing how they may actually use the memory returned. That new "must guess" requirement will break some code...
There is no "must guess". Either the driver specifies DMA_MEMORY_MAP or DMA_MEMORY_IO. If you specify DMA_MEMORY_IO then you have to use accessors for dma_alloc_coherent() memory. If you never wish to bother with it, simply specify DMA_MEMORY_MAP and take the failure on platforms which cannot comply.
Quoted 4 lines
So -- you're saying it's not a bug that a __GFP_NOFAIL|__GFP_WAIT allocation be able to fail? Curious. I'd have thought the API was clear about that. Allocating 128 MB from a 1 MB region must of course fail, but allocating one page just needs a wait/wakeup.
It can *only* happen if you specify DMA_MEMORY_EXCLUSIVE; that preempts the GFP_ flags and the application must be coded with this in mind. Otherwise it will respect the GFP_ flags.
James
Quoted 9 lines · James Bottomley
Quoted 5 lines · David Brownell
Seems unreasonable to me, unless there's also an API to change the mode of the
dma_alloc_coherent()memory from the normal "CPU can read/write as usual" to the exotic "need to use special memory accessors". (And another to report what mode the API is in at the current moment.)
Quoted 4 lines
No. That's why you specify how you'd like the memory to be treated. If you don't want the memory to be accessible only via IO accessors, then you specify
DMA_MEMORY_MAPand take the failure if the platform can't handle it.
That can work when the scope of "DMA" knowledge is just one driver ... but when that driver is plugging into a framework, it's as likely to be some other code (maybe a higher level driver) that just wants RAM address space which, for whatever reasons, is DMA-coherent. And hey, the way to get this is dma_alloc_coherent ... or in some cases, pci_alloc_consistent.
Which is why my comment was that the new feature of returning some kind of memory cookie usable on that one IBM box (etc) should just use a different allocator API. It doesn't allocate RAM "similarly to __get_free_pages"; it'd be returning something drivers can't treat as RAM.
Quoted 8 lines
Quoted 3 lines
And I don't like modal APIs like that, which is why it'd make more sense to me to have a new allocator API for this new kind of DMA memory. (Which IS for that IBM processor, yes?)
There is no "new" kind of memory. This is currently how *all* I/O memory is accessed.
DMA_MEMORY_MAPis actually the new bit since it allows I/O memory to be treated as normal memory.
This isn't I/O address space, needing PIO I/O accessors, and as returned by the new DMA_MEMORY_IO mode. (And why wouldn't ioremap already handle that?)
This is how to allocate DMA-ready buffers that have certain characteristics aren't useful only to the lowest level drivers in the stack. Drivers depend on alloc_coherent to behave in the way you (originally) said DMA_MEMORY_MAP works. The more detailed API specs (DMA-mapping.txt not DMA-API.txt) are very clear that the behavior is like RAM.
Quoted 8 lines
Quoted 4 lines
So -- you're saying it's not a bug that a __GFP_NOFAIL|__GFP_WAIT allocation be able to fail? Curious. I'd have thought the API was clear about that. Allocating 128 MB from a 1 MB region must of course fail, but allocating one page just needs a wait/wakeup.
It can *only* happen if you specify
DMA_MEMORY_EXCLUSIVE; that preempts
theGFP_flags and the application must be coded with this in mind. Otherwise it will respect theGFP_flags.
You'd have to change the spec to allow that. Wouldn't it be a lot simpler to just pass the GFP flags down to that lowlevel code, so it can eventually start doing what the highlevel code told it to do? :)
Special cases make for fragile systems.
- Dave
Quoted 7 lines · David Brownell
That can work when the scope of "DMA" knowledge is just one driver ... but when that driver is plugging into a framework, it's as likely to be some other code (maybe a higher level driver) that just wants RAM address space which, for whatever reasons, is DMA-coherent. And hey, the way to get this is
dma_alloc_coherent... or in some cases,pci_alloc_consistent.
If the driver can't cope then you *only* use DMA_MEMORY_MAPPED.
Quoted 5 lines
Which is why my comment was that the new feature of returning some kind of memory cookie usable on that one IBM box (etc) should just use a different allocator API. It doesn't allocate RAM "similarly to
__get_free_pages"; it'd be returning something drivers can't treat as RAM.
Well, I don't believe it will be necessary. However, when an actual user comes along, we'll find out.
Quoted 3 lines
This isn't I/O address space, needing PIO I/O accessors, and as returned by the new
DMA_MEMORY_IOmode. (And why wouldn't ioremap already handle that?)
The purpose of the API is to allow a mode of operation on all platforms linux supports.
Quoted 6 lines
This is how to allocate DMA-ready buffers that have certain characteristics aren't useful only to the lowest level drivers in the stack. Drivers depend on
alloc_coherentto behave in the way you (originally) saidDMA_MEMORY_MAPworks. The more detailed API specs (DMA-mapping.txt not DMA-API.txt) are very clear that the behavior is like RAM.
It is no-longer real memory once you use this API. Even if the processor can treat DMA_MEMORY_MAP memory as "real", you'll probably find that a device off on another bus cannot even see it. However, as long as you keep the memory between the processor and the device then you can treat it identical to RAM.
Quoted 10 lines
Quoted 3 lines
It can *only* happen if you specify
DMA_MEMORY_EXCLUSIVE; that preempts theGFP_flags and the application must be coded with this in mind. Otherwise it will respect theGFP_flags.You'd have to change the spec to allow that. Wouldn't it be a lot simpler to just pass the GFP flags down to that lowlevel code, so it can eventually start doing what the highlevel code told it to do? :)
Special cases make for fragile systems.
The intention of the flags option for dma_alloc_coherent() was only for memory allocation instructions; the allocation can fail for other reasons that unavailability of memory depending on how the API is implemented, so __GFP_NOFAIL doesn't actually work now in every case. Thus this doesn't actually represent a departure.
James
Quoted 11 lines · James Bottomley
Quoted 7 lines · David Brownell
That can work when the scope of "DMA" knowledge is just one driver ... but when that driver is plugging into a framework, it's as likely to be some other code (maybe a higher level driver) that just wants RAM address space which, for whatever reasons, is DMA-coherent. And hey, the way to get this is
dma_alloc_coherent... or in some cases,pci_alloc_consistent.
Quoted 1 line
If the driver can't cope then you *only* use
DMA_MEMORY_MAP
That would be the norm for all those low-level drivers, certainly. Except maybe on that one mysterious box, where the CPU can't access that memory directly ... ;)
Quoted 9 lines
Quoted 5 lines
Which is why my comment was that the new feature of returning some kind of memory cookie usable on that one IBM box (etc) should just use a different allocator API. It doesn't allocate RAM "similarly to
__get_free_pages"; it'd be returning something drivers can't treat as RAM.Well, I don't believe it will be necessary. However, when an actual user comes along, we'll find out.
OK, I can easily view DMA_MEMORY_IO as an API experiment.
Quoted 5 lines
It is no-longer real memory once you use this API. Even if the processor can treat
DMA_MEMORY_MAPmemory as "real", you'll probably find that a device off on another bus cannot even see it. However, as long as you keep the memory between the processor and the device then you can treat it identical to RAM.
I'm not sure I see what you're saying. The only guarantees on the memory are that "the" CPU and the device can both access it like memory. Other devices are out-of-scope, as is location (anywhere both can access it like normal memory, not just stuff that's "between" the two on some bus). It's DMA_MEMORY_IO that you said would not be RAM-like ("directly writable"), and would need I/O memory accessors like readl/writel/etc ... to the device it looks like normal RAM, but not to the host.
Quoted 4 lines
The intention of the flags option for
dma_alloc_coherent()was only for memory allocation instructions; the allocation can fail for other reasons that unavailability of memory depending on how the API is implemented, so__GFP_NOFAILdoesn't actually work now in every case.
I personally think __GFP_WAIT is the most important one, but some folk have other priorities. Regardless, I _was_ talking about passing flags down to the memory allocator, so it sounds like it was just an oversight in this initial version.
- Dave