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 <[email protected]> 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
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 [email protected] http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies
