Reputation: 395
I'm looking for a fast and efficient way for user-space to send I/O to my driver. One way I'd have hoped to do this, was through a shared memory ring-buffer.
In the WWDC19 presentation on System Extensions and DriverKit, at roughly 17:00, they mention an IOSharedDataQueueDispatchSource. This doesn't exist in the DriverKit API. An IODataQueueDispatchSource is available, but doesn't seem to be meant to be shared.
In the old IOKit framework, there are similar IOSharedDataQueue and IODataQueue, but they are unavailable in DriverKit.
So, what are my options for implementing a fast, efficient I/O path to my driver?
Upvotes: 4
Views: 358
Reputation: 395
The answer seems to be that DriverKit does not provide any functionality for this. So I ended up making my own ring-buffer queue.
In essence, I allocate two buffers in my driver -- a submission and a completion queue -- and share these with my user-space application through CopyClientMemoryForType
. In these buffers I have a head and tail variable and the actual memory for the queue. By atomically advancing head and tail and pushing tasks into the buffer, I can communicate between the two very efficiently. I still do a IOConnectCallStructMethod
to signal when there's new queue entries.
The code can be found here:
https://github.com/OpenMPDK/MacVFN/blob/master/MacVFN/MacVFNShared.h
Upvotes: 1
Reputation: 23438
The documentation for IODataQueueDispatchSource
literally says: (emphasis mine)
A dispatch source that manages a shared-memory data queue.
However, the documentation also says the CopyMemory()
method is private, which would prevent you from sharing the IOMemoryDescriptor
with a user client.
Fortunately, this isn't actually true in the header file that comes with the DriverKit SDK. In the documentation block it's defined as private:
, but in the IODataQueueDispatchSource_Methods
macro, it's in the public:
block. Go figure.
Internally, it uses the same Looks like the layout of the shared-memory IODataQueueMemory
data structure as the kernel's IOSharedDataQueue
, and the user space IODataQueueClient
.IODataQueueMemory
struct differs from the kernel/user space IOSharedDataQueue
/IODataQueueClient
, making it incompatible with those implementations.
Still, it's in principle possible to create the data queue in the dext, grab the memory descriptor with CopyMemory
when the user client asks to map it, and pass that mapped memory to your app/daemon. The app/daemon will need to implement its own version of the queue logic based on the IODataQueueDispatchSource
version of IODataQueueMemory
and IODataQueueEntry
structs. Far from ideal, but doable.
Upvotes: 3