An OOB-capable driver is a device driver which implements low latency operations on files like oob_read(), oob_write() and oob_ioctl(), in addition to a set of common in-band operations such as open(2), close(2), read(2), write(2), ioctl(2) etc. Out-of-band I/O requests are issued by EVL threads running in user-space. Those requests are directly handled from the out-of-band execution stage, ensuring low and bounded latency.
EVL files are kernel file objects (struct file
) which have been
advertised by an OOB-capable driver as supporting these out-of-band
I/O operations, in addition to
common in-band requests. For instance, oob_ioctl() requests may be issued for an EVL
file, like
ioctl(2) may as
well. Such advertisement usually happens in the open()
operation
handler of a character device driver by calling evl_open_file() for the new file description being
initialized. Drivers which create (pseudo-)file objects with
out-of-band support on-the-fly from their
ioctl(2) handler
may call evl_open_file() from this
context as necessary.
A typical example of the latter usage can be found in the EVL-enabled GPIOLIB
driver,
which advertises out-of-band support for files created by the
GPIO_GET_LINEEVENT_IOCTL request from the character device interface
to GPIO
lines. A
more usual way of advertising EVL files can be seen from the
latmus_open()
routine of the latmus driver
code
which supports the latmus application..
Advertise filp as a file which can be subject to out-of-band
handling. filp is a pointer to a common file description (struct file
) usually received by the open()
operation handler of a
character device driver. evl_open_file() extends this in-band description with an out-of-band context
stored at efilp. Once evl_open_file() has returned, applications can send out-of-band I/O
requests to this driver via any
file descriptor which refers to filp. evl_open_file() does not block, it merely initializes efilp,
binds it to filp then returns.
*efilp is usually part of the private data structure a driver associates with each active file, as illustrated by the following example:
#include <slab.h>
#include <evl/file.h>
struct foo_private_state {
/* ... */
struct evl_file efile;
};
static int foo_open(struct inode *inode, struct file *filp)
{
struct foo_private_state *p;
int ret;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
ret = evl_open_file(&p->efile, filp);
if (ret) {
kfree(p);
return ret;
}
filp->private_data = p;
return 0;
}
A pointer to an EVL file context (struct evl_file
), which is
initialized by this call. This memory object must live as long as
filp is valid.
The pointer to the file description structure received from the
VFS by
the open()
operation handler of a character device driver.
evl_open_file() always succeeds, returning zero in the current implementation. However, you may still want to check the return code in case error conditions surface in a later revision of this code.
This is the converse operation to evl_open_file(), which must be called by the release()
handler of an OOB-capable character device driver.
Because the in-band kernel and the EVL core run almost fully
asynchronously, some out-of-band I/O operations - which the in-band kernel does not know about -
might still be pending for efilp when the release()
handler is
invoked by the in-band kernel. For instance, some application which
issued an oob_read() request might still be waiting for data in the oob_read()
operation handler of the driver, sleeping on an EVL wait queue or a
semaphore,
when some other thread of the program
closes all the
file descriptors referring to that file, leading to the release()
handler to be called by the
VFS. In
order to track those operations, EVL maintains an internal reference
count on every device file registered with evl_open_file(). evl_release_file() waits for this reference count to reach zero
before returning to the caller, acting as a synchronization barrier
with respect to any concurrent out-of-band operation which might be
running on any CPU. When this routine returns, you can be sure that no
such operation is ongoing in the system for the released file,
therefore it can be safely dismantled afterwards.
For this reason, you do want to call evl_release_file() before the context data attached to the
file being released is freed by the release()
handler eventually
. The following example illustrates such precaution, considering that
some out-of-band operations might still be in-flight while the device
file is released by the in-band kernel, such as waiting on an EVL
semaphore.
#include <slab.h>
#include <evl/flag.h>
#include <evl/file.h>
struct foo_private_state {
/* ... */
struct evl_flag eflag;
struct evl_file efile;
};
static int foo_open(struct inode *inode, struct file *filp)
{
struct foo_private_state *p;
int ret;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
ret = evl_open_file(&p->efile, filp);
if (ret) {
kfree(p);
return ret;
}
evl_init_flag(&p->eflag);
filp->private_data = p;
return 0;
}
static int foo_release(struct inode *inode, struct file *filp)
{
struct foo_private_state *p = filp->private_data;
/*
* Flush and destroy the flag some out-of-band operation(s)
* might still be pending on. Some EVL thread(s) might be
* unblocked from oob_read() as a result of this call.
*/
evl_destroy_flag(&p->eflag);
/*
* Synchronize with these operations. Unblocked threads will drop
* any pending reference on the file being released, eventually
* allowing this call to return.
*/
evl_release_file(&p->efile);
/*
* Neither in-band nor out-of-band users of this file anymore
* for sure, we can free the per-file private context.
*/
kfree(p);
return 0;
}
A pointer to the EVL file context (struct evl_file
), which was
initialized by a previous call to evl_open_file().
evl_release_file() may block before releasing the file until all references to efilp are gone, which indicates that no out-of-band operation is undergoing for this EVL file.
Search for the EVL file indexed on fd and get a reference on it atomically, which prevents its release by evl_release_file() until the last reference is dropped by a call to evl_put_file().
Internally, the EVL core holds a reference on any file subject to an out-of-band request across the call to the corresponding operation handler in the driver, so that such file does not go stale unexpectedly while the request is still ongoing. You may want to get additional references on EVL files from any EVL driver which receives file descriptors directly from applications via ioctl(2) arguments. Such reference should be held as long as the operation on the underlying EVL file is not complete.
The implementation of the polling services in the EVL core illustrates how evl_get_file() is used for retrieving EVL file descriptions from descriptors passed by the application.
A valid file descriptor referring to the EVL file.
This call returns a valid pointer to an active EVL file - such as efilp passed to evl_open_file())
Get a reference on the EVL file efilp. This is the core helper which actually gets a reference on any EVL file. Once it has returned, the file cannot be released by evl_release_file() until the reference is dropped by a converse call to evl_put_file().
A pointer to a valid EVL file description. This description was originally initialized by a call to evl_open_file().
Drop a reference on an EVL file previously obtained by a call to evl_get_fileref() or evl_get_file().
If the reference count is zero at the time evl_release_file() is called for efilp, the release is performed immediately.