Kernel panic: attempting to write / read on a small tty driver

I am starting to program Linux and trying to use some examples of device drivers during practice. The code below (shortened version of tiny_tty.c ) loads fine with insmod, and I can see it in /proc/tty/drivers , /proc/modules , and the device nodes are created in / dev. When I try to write to the device file, for example, echo "abcd" > /dev/ttyms0 (I hope this is good) or reads like cat /dev/ttyms0 , the kernel panics with a call trace on the screen. I am using the 3.5.0 kernel under Ubuntu. Unfortunately, I cannot fix the trace, because when it is panicky, I have no choice but to reboot with the power button. I believe that the problem with the timer is here, since the trace shows the line above: "*kernel bug at /build/buildd/linux-3.5.0/kernel/timer.c:901* ", then the trace of the call, followed by "*EIP is at add_timer+0x18/0x20*" The following is the complete code. Any guidance is greatly appreciated pending.

May 10, 2013 . I tried to initialize the timer in an open function, and this time below the call trace is shown for "kernel panic - not synchronization: a fatal error occurred in interrupting the interrupt, switching to the text console":

 update_sd_lb_stats+0xcd/0x4b0 find_busiest_group+0x2e/0x420 enqueue_entity+0xcb/0x510 load_balance+0x7e/0x5e0 rebalance_domains+0xed/0x150 __do_softirq+0xdb/0x180 local_bh_enable_ip+0x90/0x90 <IRQ> copy_to_user0x41/0x60 sys_gettimeofday+0x2a/0x70 sysenter_do_call0x12/0x20 #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.h> #include <linux/wait.h> #include <linux/errno.h> #include <linux/slab.h> /* kmalloc() */ #include <linux/tty_driver.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/mutex.h> #include <linux/serial.h> #include <linux/sched.h> #include <asm/uaccess.h> #include <asm/termios.h> MODULE_LICENSE("GPL"); #define MS_TTY_MAJOR 250 //test value #define MS_TTY_NUM_DEV 2 #define DELAY_TIME HZ * 2 /* 2 seconds per character */ #define TINY_DATA_CHARACTER 't' static int major_num; //static int minor_num=0; //static int num_tty_dev=2; /* Below structure is a wrapper for device specific fields */ struct ms_tty_serial { struct tty_struct *tty; /* pointer to the tty for this device */ int open_count; /* number of times this port has been opened */ struct semaphore sem; /* locks this structure */ struct timer_list *timer; }; static struct ms_tty_serial *ms_tty_table[MS_TTY_NUM_DEV]; /* initially all NULL */ static void ms_tty_timer(unsigned long timer_data) { struct ms_tty_serial *ms_ptr = (struct ms_tty_serial *)timer_data; struct tty_struct *tty; char data[1] = {TINY_DATA_CHARACTER}; int data_size = 1; if (!ms_ptr) return; tty = ms_ptr->tty; tty->low_latency=1; /* send the data to the tty layer for users to read. This doesn't * actually push the data through unless tty->low_latency is set */ tty_buffer_request_room (tty, data_size); tty_insert_flip_string(tty, data, data_size); tty_flip_buffer_push(tty); /* resubmit the timer again */ ms_ptr->timer->expires = jiffies + DELAY_TIME; add_timer(ms_ptr->timer); } //// Define the open function //// static int tty_ms_open(struct tty_struct *tty_this, struct file *file_this) { printk(KERN_ALERT "tty_ms driver: OPENED ...\n"); struct ms_tty_serial *ms_ptr; struct timer_list *timer; int index; /* initialize the pointer in case something fails */ tty_this->driver_data = NULL; /* get the serial object associated with this tty pointer */ index = tty_this->index; ms_ptr = ms_tty_table[index]; if (ms_ptr == NULL) { /* first time accessing this device, create it */ ms_ptr = kmalloc(sizeof(*ms_ptr), GFP_KERNEL); if (!ms_ptr) return -ENOMEM; // init_MUTEX(&ms_ptr->sem); /* didn't work for this kernel version 3.5.0 */ #ifndef init_MUTEX /* sema_init is to be used for kernel 2.6.37 and above */ sema_init(&ms_ptr->sem,1); #else init_MUTEX(&ms_ptr->sem); #endif ms_ptr->open_count = 0; ms_ptr->timer = NULL; ms_tty_table[index] = ms_ptr; } down(&ms_ptr->sem); /* save our structure within the tty structure */ tty_this->driver_data = ms_ptr; ms_ptr->tty = tty_this; ms_ptr->filp = file_this; // to be tried ++ms_ptr->open_count; if (ms_ptr->open_count == 1) { /* this is the first time this port is opened */ /* do any hardware initialization needed here */ /* create timer and submit it */ if (!ms_ptr->timer) { timer = kmalloc(sizeof(*timer), GFP_KERNEL); if (!timer) { up(&ms_ptr->sem); return -ENOMEM; } ms_ptr->timer = timer; } init_timer (ms_ptr->timer); // to be tried ms_ptr->timer->data = (unsigned long )ms_ptr; ms_ptr->timer->expires = jiffies + DELAY_TIME; ms_ptr->timer->function = ms_tty_timer; add_timer(ms_ptr->timer); } up(&ms_ptr->sem); return 0; } //// Define the close function //// static void do_close(struct ms_tty_serial *ms_ptr) { down(&ms_ptr->sem); if (!ms_ptr->open_count) { /* port was never opened */ goto exit; } --ms_ptr->open_count; if (ms_ptr->open_count <= 0) { /* The port is being closed by the last user. */ /* Do any hardware specific stuff here */ /* shut down our timer */ del_timer(ms_ptr->timer); } exit: up(&ms_ptr->sem); } static void tty_ms_close(struct tty_struct *tty_this, struct file *file_this) { printk(KERN_ALERT "tty_ms driver: CLOSING ...\n"); struct ms_tty_serial *ms_ptr = tty_this->driver_data; if (ms_ptr) do_close(ms_ptr); } //// Define the write function //// static int tty_ms_write(struct tty_struct *tty_this, const unsigned char *tty_buff, int count) { printk(KERN_ALERT "tty_ms driver: WRITING ...\n"); struct ms_tty_serial *ms_ptr = tty_this->driver_data; int i; int retval = -EINVAL; if (!ms_ptr) return -ENODEV; down(&ms_ptr->sem); if (!ms_ptr->open_count) /* port was not opened */ { up(&ms_ptr->sem); return retval; } /* fake sending the data out a hardware port by * writing it to the kernel debug log. */ printk(KERN_DEBUG "%s - ", __FUNCTION__); for (i = 0; i < count; ++i) printk("%02x ", tty_buff[i]); printk("\n"); return 0; } // Define the operations for tty driver in tty_operations struct // static struct tty_operations tty_ms_ops = { .open = tty_ms_open, .close = tty_ms_close, .write = tty_ms_write, //.set_termios = tty_ms_set_termios, }; ///////////////////////////////////////// Module Initialization Starts //////////////////////////////////// static struct tty_driver *tty_ms_driver; static int tty_ms_init(void) { // static int result; static int retval,iter; // Allocate the tty_driver struct for this driver // tty_ms_driver = alloc_tty_driver(MS_TTY_NUM_DEV); if (!tty_ms_driver) return -ENOMEM; // Error NO Memory , allocation failed else printk(KERN_INFO "tty_driver structure allocated..!!"); //debug line // Initialize the allocated tty_driver structure // tty_ms_driver->magic=TTY_DRIVER_MAGIC; tty_ms_driver->owner = THIS_MODULE; tty_ms_driver->driver_name = "tty_ms"; tty_ms_driver->name = "ttyms"; tty_ms_driver->major = MS_TTY_MAJOR, tty_ms_driver->type = TTY_DRIVER_TYPE_SERIAL, tty_ms_driver->subtype = SERIAL_TYPE_NORMAL, tty_ms_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV, tty_ms_driver->init_termios = tty_std_termios; tty_ms_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(tty_ms_driver, &tty_ms_ops); printk(KERN_INFO "allocated tty_driver structure -INITIALIZED."); //debug line //// Register this driver with the tty core //// retval = tty_register_driver(tty_ms_driver); if (retval) { printk(KERN_ERR "failed to register tty_ms driver\n tty registration returned %d", retval); put_tty_driver(tty_ms_driver); return retval; } //// Register the tty devices(nodes) with the tty core //// for (iter = 0; iter < MS_TTY_NUM_DEV ; ++iter) tty_register_device(tty_ms_driver, iter, NULL); return 0; // All initializations done } // init func ends ///////////////////////////////////////// Module Initialization Ends //////////////////////////////////// ///////////////////////////////////////// Module cleanup Starts //////////////////////////////////// static void tty_ms_terminate(void) { static int iter; struct ms_tty_serial *tty_ser; printk(KERN_ALERT "tty_ms driver: Unloading...\n"); for(iter=1;iter<=MS_TTY_NUM_DEV;iter++) tty_unregister_device(tty_ms_driver,iter); //unregister all the devices, from tty layer tty_unregister_driver(tty_ms_driver); // Now unregister the driver from tty layer /* shut down all of the timers and free the memory */ for (iter = 0; iter < MS_TTY_NUM_DEV; ++iter) { tty_ser = ms_tty_table[iter]; if (tty_ser) { /* close the port */ while (tty_ser->open_count) do_close(tty_ser); /* shut down our timer and free the memory */ del_timer(tty_ser->timer); kfree(tty_ser->timer); kfree(tty_ser); ms_tty_table[iter] = NULL; } } dev_t devno=MKDEV(major_num,0); // wrap major/minor numbers in a dev_t structure , to pass for deassigning. unregister_chrdev_region(devno,MS_TTY_NUM_DEV); } ///////////////////////////////////////// Module cleanup ends //////////////////////////////////// module_init(tty_ms_init); module_exit(tty_ms_terminate); 
+4
source share

Source: https://habr.com/ru/post/1479815/


All Articles