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