4
votes

I'm writing a Linux kernel module to read out a GPS device (a u-blox NEO-7) via USB by using the book Linux Device Drivers.

I already can probe and read out data from the device successfully. But, there is a problem when reading the device with multiple applications simultaneously (I used "cat /dev/ublox" to read indefinitely). When the active/reading applications is cancelled via "Ctrl + C", the next reading attempt from the other application fails (exactly method call usb_submit_urb(...) returns -EINVAL).

I use following ideas for my implementation:

  • The kernel module methods should be re-entrant. Therefore, I use a mutex to protect critical sections. E.g. allowing only one reader simultaneously.
  • To safe ressources, I reuse the struct urb for different reading requests (see an explanation)
  • Device-specific data like USB endpoint address and so on is held in a device-specific struct called ublox_device.
  • After submitting the USB read request, the calling process is sent to sleep until the asynchronous complete handler is called.

I verified that the ideas are implemented correctly: I have run two instances of "cat /dev/ublox" simultaneously and I got the correct output (only one instance accessed the critical read section at a time). And also reusing the "struct urb" is working. Both instances read out data alternatively.

The problem only occurs if the currently active instance is cancelled via "Ctrl + C". I can solve the problem by not reusing the "struct urb" but I would like to avoid that. I.e. by allocating a new "struct urb" for each read request via usb_alloc_urb(...) (usually it is allocated once when probing the USB device).

My code follows the USB skeleton driver from Greg Kroah-Hartman who also reuse the "struct urb" for different reading requests.

Maybe someone has a clue what's going wrong here.

The complete code can be found on pastebin. Here is a small excerpt of the read method and the USB request complete handler.

static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos)
{
        struct ublox_device *ublox_device = file->private_data;
        ...
        return_value = mutex_lock_interruptible(&ublox_device->bulk_in_mutex);
        if (return_value < 0)
                return -EINTR;
        ...
retry:
        usb_fill_bulk_urb(...);

        ublox_device->read_in_progress = 1;

        /* Next call fails if active application is cancelled via "Ctrl + C" */   
        return_value = usb_submit_urb(ublox_device->bulk_in_urb, GFP_KERNEL);
        if (return_value) {
                printk(KERN_ERR "usb_submit_urb(...) failed!\n");
                ublox_device->read_in_progress = 0;
                goto exit;
        }

        /* Go to sleep until read operation has finished */
        return_value = wait_event_interruptible(ublox_device->bulk_in_wait_queue, (!ublox_device->read_in_progress));
        if (return_value < 0)
                goto exit;
        ...
exit:
        mutex_unlock(&ublox_device->bulk_in_mutex);
        return return_value;
}

static void ublox_read_bulk_callback(struct urb *urb)
{
        struct ublox_device *ublox_device = urb->context;
        int status = urb->status;

        /* Evaluate status... */
        ...
        ublox_device->transferred_bytes = urb->actual_length;
        ublox_device->read_in_progress = 0;

        wake_up_interruptible(&ublox_device->bulk_in_wait_queue);
}
1
I think this could be a tough nut to crack... "the next reading attempt from the other application fails" -> and all subsequent reads, or just the next one? If the latter, why not just place a catch in it? I'm guessing it's actually the former, since you say you "can solve the problem by not reusing the struct urb...via usb_alloc_urb(...)", in which case you could catch EINVAL (is there any other reason you know of that would return this?) and try again after reallocating ublox_device->bulk_in_urb. Finding a complete explanation may take a lot of work, unfortunately.CodeClown42
The next read attempt, i.e. call to usb_submit_urb(...), fails with -EINVAL. This error is passed to the calling application ("cat") causing that application to stop immediately. A following invocation of "cat /dev/ublox" works properly. I also guess that the former call leaves the struct urb in a messy state. Maybe something strange happens when the current USB request is cancelled. I hoped that I would misuse the Linux USB API, but it seems to be tricky. :(Benedikt
I can't say you haven't (I'm no expert on it); I'm just guessing this is the kind of issue that's undocumented and if it seems to be that the struct is defunct, the struct is defunct, and reallocating under those (unusual) circumstances doesn't seem like a big deal. This question is going to get migrated to S.O.; if you don't get an answer there, you may want to try the LKML.CodeClown42

1 Answers

1
votes

Now, I allocate a new struct urb for each read request. This avoids the problem with the messed up struct urb after an active read request is cancelled by the calling application. The allocated struct is freed in the complete handler.

I will come back to LKML when I optimize my code. For now, it is okay to allocate a new struct urb for each single read request. The complete code of the kernel module is on pastebin.

static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos)
{
    struct ublox_device *ublox_device = file->private_data;
    ...
retry:
    ublox_device->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); 
    ...
    usb_fill_bulk_urb(...);
    ...
    return_value = usb_submit_urb(ublox_device->bulk_in_urb, GFP_KERNEL);
    ...
}

static void ublox_read_bulk_callback(struct urb *urb)
{
    struct ublox_device *ublox_device = urb->context;
    ...
    usb_free_urb(ublox_device->bulk_in_urb);
    ...
}