Tuesday, February 15, 2011

A not-so-standard non-blocking I/O implementation

It is not easy to write a proc file module, or a char device, or other similar fs related stuffs with non-blocking I/O support. But if it happens that you have full control of both userspace and kernel space code, a tricky approach can be made to do the non-blocking thing.

Simply, return -EAGAIN in the read syscall implementation immediately once the data is unavailable. Like here:
static int reqfs_read(char *buf, char **bufloc,
                      off_t offset, int buflen,
                      int *eof, void *data)
{
    int ret;
    struct list_head *r;
    struct kgpu_req *req = NULL;
    ret = 0;

    spin_lock(&reqlock);

    if (!list_empty(&reqs)) {
        r = reqs.next;
        list_del(r);
        req = list_entry(r, struct kgpu_req, list);
        if (req) {
            copy_to_user(buf, (char*)&(req->kureq), sizeof(struct ku_request));
            ret = sizeof(struct ku_request);
        }
    } else {
        ret = -EAGAIN;
    }

    spin_unlock(&reqlock);

    if (ret > 0 && req) {
        spin_lock(&rtdreqlock);

        INIT_LIST_HEAD(&req->list);
        list_add_tail(&req->list, &rtdreqs);

        spin_unlock(&rtdreqlock);
    }

    return ret;
}
Ignore the lock and list stuffs. If no data available, which is indicated by an empty list, the read of a proc fs item will return -EAGAIN to let the userspace code try again. In the userspace, read syscall will return -1, and the errno will be EAGAIN, the return value and errno must be checked explicitly to ensure the try-again response not to be treated as a fault error.

No comments: