Problem with line discipline in new kernels

Margarita Glushkin rutigl at gmail.com
Thu Aug 13 09:00:58 EDT 2015


I have a linux kernel driver for serial device, which uses line discipline 
and char device. Driver works with all old kernels, starting from 3.8 this 
driver still works, but when I unload it and load again to the memory 
(modprobe -r bpsctl_mod, after modprobe bpsctl_mod), it crashes the kernel. 
It can't unregister line discipline, because this line discipline is busy. 
I use system device, i.e., ttyS0. Here is problematic code:

static struct file* bps_file_open(const char* path, int flags, int rights)
{
    struct file* filp = NULL;    
    mm_segment_t oldfs;    
    oldfs = get_fs();    
    set_fs(get_ds());    
    filp = filp_open(path, flags, rights);    
    set_fs(oldfs);    
    if (IS_ERR(filp))
        return NULL;    

    return filp;
}

int register_ldisc(void)
{
    int           status;
    // Fill in our line protocol discipline, and register it
    extbp_drv.ebtty_ldisc.magic = TTY_LDISC_MAGIC;
    extbp_drv.ebtty_ldisc.name  = BP_LDISC_NAME;
    extbp_drv.ebtty_ldisc.flags = 0;
    extbp_drv.ebtty_ldisc.open  = ebtty_open;
    extbp_drv.ebtty_ldisc.close = ebtty_close;
    extbp_drv.ebtty_ldisc.read  = NULL;
    extbp_drv.ebtty_ldisc.write = NULL;
    extbp_drv.ebtty_ldisc.ioctl =
    (int (*)(struct tty_struct *, struct file *,
        unsigned int, unsigned long)) ebtty_ioctl;
    extbp_drv.ebtty_ldisc.poll  = NULL;
    extbp_drv.ebtty_ldisc.receive_buf  = ebtty_receive_buf;
    extbp_drv.ebtty_ldisc.write_wakeup = NULL; //ebtty_write_wakeup;
    if ((status = tty_register_ldisc(extbp_drv.line_disc,
                                     &extbp_drv.ebtty_ldisc)) == 0) {
        extbp_drv.drv_state |= ST_LDISC_REGISTRATED;
    }
    return status;
}

int ebtty_init (void)
{
    int status = -1;
    if (register_ldisc())
    {
        err_print(KERN_ERR, DBG_ERROR,
                  ("Can't register line discipline (%d).\n", line_disc));
        goto ebtty_init_exit;
    }    
    status = 0;
ebtty_init_exit:
    if (status)
    {
        dbg_print(KERN_DEBUG, DBG_ERROR, ("Module initialization failed.
\n"));
        cleanup();
    }
    return status;
}

static void cleanup(void)
{
    int status;
    if ((status = tty_unregister_ldisc(extbp_drv.line_disc)) != 0)
    {
        err_print(KERN_ERR, DBG_ERROR,
            ("Can't unregister line discipline (err = %d)\n", status));
    }
}

int init_ebtty_module(void)
{
    memset(&port_name_path, 0, sizeof(port_name_path));
    printk(BSEM_DRV_NAME BP_VER);
    sprintf((char *)&port_name_path, "//dev//%s", port_name);
    fd=bps_file_open((char *)&port_name_path, 0, O_RDWR);
    memset((void*)&extbp_drv, 0, sizeof(struct ext_bypass_drv));
    extbp_drv.drv_name      = BP_MOD_NAME;
    extbp_drv.dev_name      = port_name;
    extbp_drv.line_disc     = line_disc;
    extbp_drv.wait_data     = LAST_CHAR;
    extbp_drv.drv_state     = 0;
    init_waitqueue_head(&extbp_drv.read_wait);

    if (!ebtty_init())
    {
        extbp_drv.ebtty_major = register_chrdev(0, extbp_drv.drv_name,   
            &ebtty_fops);
        if (extbp_drv.ebtty_major < 0)
        {
            err_print(KERN_ERR, DBG_ERROR,
                      ("can't get major %d\n", extbp_drv.ebtty_major));
            return extbp_drv.ebtty_major;
        }        
        if (fd != NULL)
        {
            if (!(bps_vfs_ioctl(fd, TIOCSETD, (unsigned long)&line_disc)))
            {
                bps_start_flag = 1;
            }            
            return 0;
        }
     }
     else
     {
        if (fd)
            filp_close(fd, NULL);
        cleanup();
        return -1;
     }
}

void exit_ebtty_module(void)
{
    if (fd)
    {
        filp_close(fd, NULL);
        fd = 0;
    }   
    cleanup();
}

Please advice, maybe I should use something else, instead of line 
discipline and char device, or maybe I should register and unregister them 
other way?




More information about the Kernelnewbies mailing list