Re: How to handle Hotplug with UIO userspace driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Sorry, sent it too soon.

Here's the attached driver and userspace app for simulating a crash.
You can run make to build both.

Thanks,
-mandeep


On Mon, Dec 8, 2014 at 3:24 PM, Mandeep Sandhu
<mandeepsandhu.chd@xxxxxxxxx> wrote:
> Hi Greg,
>
>>> I saw a patch for this very situation (UIO & hotplug) being discussed on LKML
>>> almost 4 yrs back, although I don't see it in my kernel version - 3.16.0
>>> (Ubuntu 3.16.0-24-generic).
>>> The LKML link:
>>>
>>> https://lkml.org/lkml/2010/9/20/21
>>>
>>> Wouldn't this solve the issue? I wonder why it didn't make it into mainline?
>>
>> No idea, that UIO maintainer must be really lazy and never apply patches :)
>>
>> Try that series on your kernel and see what happens.  If it works,
>> please resend that patch series.
>
> I tried out a dummy uio driver with a userspace program accessing the
> mmaped space (attached). On doing an unregister of the uio device, I
> see a crash/bug being triggered. Sometimes the kernel continues to
> run, sometimes failing during rmmod of my module, sometimes locks up
> during reboot (trying this on VirtualBox VM) and other times there's
> no crash at all (but I'm just getting lucky).
>
> Although with the patch applied, the user process gets a SIGBUS and
> exits when I do an uio_unregister_device() call in my driver. So I
> guess this patch is needed.
>
>
> A generic question, if I have to apply the patches and do a sanity
> build, which branch should I be applying the patch to? linux-next?
> linux-stable?
>
> Thanks,
> -mandeep
>
>
>>
>> thanks,
>>
>> greg k-h

Attachment: Makefile
Description: Binary data

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <string.h>

#define WAIT_FOR_INTERRUPT

int main()
{
    int uiofd, intr_cnt = 0;
    void *map_addr;
    fd_set readset;

    uiofd = open("/dev/uio0", O_RDWR);
    if (uiofd < 0) {
        perror("uio open:");
        return errno;
    }

    long page_size = getpagesize();
    map_addr = mmap(NULL, page_size,
                    PROT_READ | PROT_WRITE,
                    MAP_SHARED, uiofd, 0);

    // Hammer away!
    while (1) {
#ifdef WAIT_FOR_INTERRUPT
        FD_ZERO(&readset);
        FD_SET(uiofd, &readset);
        if (select(uiofd + 1, &readset, NULL, NULL, NULL) <= 0) {
            perror("select:");
            break;
        }
        if (FD_ISSET(uiofd, &readset)) {
            if (read(uiofd, &intr_cnt, 4) != 4) {
                perror("uio read:");
                break;
            }
            printf("intr count %d\n", intr_cnt);
            memset(map_addr, 0, page_size);
        }
#else
        memset(map_addr, 0, page_size);
#endif
    }
    
    munmap(map_addr, page_size);
    close(uiofd);
    return 0;
}

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/uio_driver.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/types.h>

#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/timer.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mandeep Sandhu");
MODULE_DESCRIPTION("Dummy UIO kernel driver");

static struct uio_info* info;
static struct platform_device *pdev;
static struct task_struct *ts;
static struct timer_list fake_intr_timer;

static int msec_unreg_delay = 3000; // 3 secs
static int msec_uio_intr_delay = 1000; // 1 sec

void unregister_uio(void)
{
    if (info) {
        pr_info("Unregistering uio\n");
        uio_unregister_device(info);
        kfree((void *)info->mem[0].addr);
        kfree(info);
        info = 0;
    }
    pr_info("Delting fake interrupt timer\n");
    del_timer(&fake_intr_timer);
}

int kthread_fn(void *data)
{
    pr_info("Sleeping for %d secs\n", (msec_unreg_delay)/1000);
    msleep(msec_unreg_delay);

    if (kthread_should_stop())
        return 0;

    unregister_uio();
    pr_info("Exiting thread\n");
    ts = 0;
    return 0;
}

void fake_intr_timer_cb(unsigned long data)
{
    if (!ts)
        return;

    pr_info("Firing fake interrupt\n");
    uio_event_notify(info);
    // Setup timer to fire again
    mod_timer(&fake_intr_timer,
               jiffies + msecs_to_jiffies(msec_uio_intr_delay));
}

static int uio_dummy_open(struct uio_info *info, struct inode *inode)
{
    pr_info("%s called\n", __FUNCTION__);
    return 0;
}

static int uio_dummy_release(struct uio_info *info, struct inode *inode)
{
    pr_info("%s called\n", __FUNCTION__);
    return 0;
}

static int __init uio_dummy_init(void)
{
    printk(KERN_INFO "Dummy uio driver!\n");

    pdev = platform_device_register_simple("dummy_platform_device",
                                            0, NULL, 0);
    if (IS_ERR(pdev)) {
        pr_err("Failed to register platform device.\n");
        return -EINVAL;
    }

    info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
    
    if (!info)
        return -ENOMEM;

    info->name = "dummy_uio_driver";
    info->version = "0.1";
    info->mem[0].addr = (phys_addr_t) kzalloc(PAGE_SIZE, GFP_ATOMIC);
    if (!info->mem[0].addr)
        goto uiomem;
    info->mem[0].memtype = UIO_MEM_LOGICAL;
    info->mem[0].size = PAGE_SIZE;
    info->irq = UIO_IRQ_CUSTOM;
    info->handler = 0;
    info->open = uio_dummy_open;
    info->release = uio_dummy_release;
	
    if(uio_register_device(&pdev->dev, info)) {
        pr_err("Unable to register UIO device!\n");
        goto devmem;
    } else {
        pr_info("Successfully registered UIO device!\n");
    }

    pr_info("Starting uio unreg kthread\n");
    ts = kthread_run(kthread_fn, NULL, "uio_unreg_kthread");

    setup_timer(&fake_intr_timer, fake_intr_timer_cb, 0 );
    if (mod_timer(&fake_intr_timer, (jiffies +
                                     msecs_to_jiffies(msec_uio_intr_delay)))) {
        pr_err("Error setting up fake interrupt timer");
        goto devmem;
    }
    return 0;

devmem:
    kfree((void *)info->mem[0].addr);
uiomem:
    kfree(info);
    
    return -ENODEV;
}

static void __exit uio_dummy_cleanup(void)
{
    printk(KERN_INFO "Cleaning up module.\n");

    if (ts) {
        del_timer(&fake_intr_timer);
        kthread_stop(ts);
        unregister_uio();
    }

    if (pdev)
        platform_device_unregister(pdev);
}

module_init(uio_dummy_init);
module_exit(uio_dummy_cleanup);
_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@xxxxxxxxxxxxxxxxx
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux