Commit 4be124a7 authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN: lock only used driver

We used to lock (ind mod use count) all drivers just in case, but
it makes more sense to only lock the one we're just using, in
particular since the old scheme was rather broken when insmod'ing
a new driver later.
parent 5480f44d
...@@ -34,6 +34,9 @@ MODULE_LICENSE("GPL"); ...@@ -34,6 +34,9 @@ MODULE_LICENSE("GPL");
isdn_dev *dev; isdn_dev *dev;
static void isdn_lock_driver(struct isdn_driver *drv);
static void isdn_unlock_driver(struct isdn_driver *drv);
static void isdn_register_devfs(int); static void isdn_register_devfs(int);
static void isdn_unregister_devfs(int); static void isdn_unregister_devfs(int);
...@@ -109,6 +112,9 @@ do_event_cb(struct isdn_slot *slot, int pr, void *arg) ...@@ -109,6 +112,9 @@ do_event_cb(struct isdn_slot *slot, int pr, void *arg)
static int static int
slot_bind(struct fsm_inst *fi, int pr, void *arg) slot_bind(struct fsm_inst *fi, int pr, void *arg)
{ {
struct isdn_slot *slot = fi->userdata;
isdn_lock_driver(slot->drv);
fsm_change_state(fi, ST_SLOT_BOUND); fsm_change_state(fi, ST_SLOT_BOUND);
return 0; return 0;
...@@ -287,6 +293,7 @@ slot_icall(struct fsm_inst *fi, int pr, void *arg) ...@@ -287,6 +293,7 @@ slot_icall(struct fsm_inst *fi, int pr, void *arg)
isdn_ctrl *ctrl = arg; isdn_ctrl *ctrl = arg;
int retval; int retval;
isdn_lock_driver(slot->drv);
fsm_change_state(fi, ST_SLOT_IN); fsm_change_state(fi, ST_SLOT_IN);
slot_debug(fi, "ICALL: %s\n", ctrl->parm.num); slot_debug(fi, "ICALL: %s\n", ctrl->parm.num);
if (dev->global_flags & ISDN_GLOBAL_STOPPED) if (dev->global_flags & ISDN_GLOBAL_STOPPED)
...@@ -311,6 +318,7 @@ slot_in_dhup(struct fsm_inst *fi, int pr, void *arg) ...@@ -311,6 +318,7 @@ slot_in_dhup(struct fsm_inst *fi, int pr, void *arg)
{ {
struct isdn_slot *slot = fi->userdata; struct isdn_slot *slot = fi->userdata;
isdn_unlock_driver(slot->drv);
fsm_change_state(fi, ST_SLOT_NULL); fsm_change_state(fi, ST_SLOT_NULL);
do_event_cb(slot, pr, arg); do_event_cb(slot, pr, arg);
return 0; return 0;
...@@ -322,6 +330,8 @@ slot_unbind(struct fsm_inst *fi, int pr, void *arg) ...@@ -322,6 +330,8 @@ slot_unbind(struct fsm_inst *fi, int pr, void *arg)
struct isdn_slot *slot = fi->userdata; struct isdn_slot *slot = fi->userdata;
isdn_ctrl cmd; isdn_ctrl cmd;
isdn_unlock_driver(slot->drv);
fsm_change_state(fi, ST_SLOT_NULL);
strcpy(slot->num, "???"); strcpy(slot->num, "???");
cmd.parm.num[0] = '\0'; cmd.parm.num[0] = '\0';
isdn_slot_command(slot, ISDN_CMD_SETEAZ, &cmd); isdn_slot_command(slot, ISDN_CMD_SETEAZ, &cmd);
...@@ -522,6 +532,9 @@ get_drv_by_nr(int di) ...@@ -522,6 +532,9 @@ get_drv_by_nr(int di)
unsigned long flags; unsigned long flags;
struct isdn_driver *drv; struct isdn_driver *drv;
if (di < 0)
return NULL;
spin_lock_irqsave(&drivers_lock, flags); spin_lock_irqsave(&drivers_lock, flags);
drv = drivers[di]; drv = drivers[di];
if (drv) if (drv)
...@@ -977,62 +990,26 @@ static isdn_divert_if *divert_if; /* = NULL */ ...@@ -977,62 +990,26 @@ static isdn_divert_if *divert_if; /* = NULL */
static int isdn_wildmat(char *s, char *p); static int isdn_wildmat(char *s, char *p);
void static void
isdn_lock_drivers(void) isdn_lock_driver(struct isdn_driver *drv)
{ {
isdn_ctrl cmd; isdn_ctrl cmd;
unsigned long flags;
int i;
spin_lock_irqsave(&drivers_lock, flags);
for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
if (!drivers[i])
continue;
cmd.driver = i; cmd.driver = drv->di;
cmd.arg = 0; cmd.arg = 0;
cmd.command = ISDN_CMD_LOCK; cmd.command = ISDN_CMD_LOCK;
__drv_command(drivers[i], &cmd); __drv_command(drv, &cmd);
drivers[i]->locks++;
}
spin_unlock_irqrestore(&drivers_lock, flags);
}
void
isdn_MOD_INC_USE_COUNT(void)
{
MOD_INC_USE_COUNT;
isdn_lock_drivers();
} }
void static void
isdn_unlock_drivers(void) isdn_unlock_driver(struct isdn_driver *drv)
{ {
isdn_ctrl cmd; isdn_ctrl cmd;
unsigned long flags;
int i;
spin_lock_irqsave(&drivers_lock, flags); cmd.driver = drv->di;
for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
if (!drivers[i])
continue;
if (drivers[i]->locks > 0) {
cmd.driver = i;
cmd.arg = 0; cmd.arg = 0;
cmd.command = ISDN_CMD_UNLOCK; cmd.command = ISDN_CMD_UNLOCK;
__drv_command(drivers[i], &cmd); __drv_command(drv, &cmd);
drivers[i]->locks--;
}
}
spin_unlock_irqrestore(&drivers_lock, flags);
}
void
isdn_MOD_DEC_USE_COUNT(void)
{
MOD_DEC_USE_COUNT;
isdn_unlock_drivers();
} }
#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
...@@ -1461,47 +1438,42 @@ static struct file_operations isdn_status_fops = ...@@ -1461,47 +1438,42 @@ static struct file_operations isdn_status_fops =
*/ */
static int static int
isdn_ctrl_open(struct inode *ino, struct file *filep) isdn_ctrl_open(struct inode *ino, struct file *file)
{ {
unsigned int minor = minor(ino->i_rdev); unsigned int minor = minor(ino->i_rdev);
int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
int retval = 0; struct isdn_driver *drv;
if (drvidx < 0) { drv = get_drv_by_nr(drvidx);
retval = -ENODEV; if (!drv)
goto out; return -ENODEV;
}
isdn_lock_drivers();
out: isdn_lock_driver(drv);
return retval;
file->private_data = drv;
return 0;
} }
static int static int
isdn_ctrl_release(struct inode *ino, struct file *filep) isdn_ctrl_release(struct inode *ino, struct file *file)
{ {
unsigned int minor = minor(ino->i_rdev); struct isdn_driver *drv = file->private_data;
int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
if (drvidx < 0) {
isdn_BUG();
goto out;
}
if (dev->profd == current) if (dev->profd == current)
dev->profd = NULL; dev->profd = NULL;
isdn_unlock_drivers(); isdn_unlock_driver(drv);
put_drv(drv);
out:
return 0; return 0;
} }
static ssize_t static ssize_t
isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off) isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off)
{ {
DECLARE_WAITQUEUE(wait, current); struct isdn_driver *drv = file->private_data;
unsigned int minor = minor(file->f_dentry->d_inode->i_rdev); unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);
int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); DECLARE_WAITQUEUE(wait, current);
unsigned long flags; unsigned long flags;
int len = 0; int len = 0;
...@@ -1509,18 +1481,14 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off) ...@@ -1509,18 +1481,14 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off)
if (off != &file->f_pos) if (off != &file->f_pos)
return -ESPIPE; return -ESPIPE;
if (drvidx < 0) { if (!drv->interface->readstat) {
isdn_BUG();
return -ENODEV;
}
if (!drivers[drvidx]->interface->readstat) {
isdn_BUG(); isdn_BUG();
return 0; return 0;
} }
add_wait_queue(&drivers[drvidx]->st_waitq, &wait); add_wait_queue(&drv->st_waitq, &wait);
for (;;) { for (;;) {
spin_lock_irqsave(&stat_lock, flags); spin_lock_irqsave(&stat_lock, flags);
len = drivers[drvidx]->stavail; len = drv->stavail;
spin_unlock_irqrestore(&stat_lock, flags); spin_unlock_irqrestore(&stat_lock, flags);
if (len > 0) if (len > 0)
break; break;
...@@ -1535,7 +1503,7 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off) ...@@ -1535,7 +1503,7 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off)
schedule(); schedule();
} }
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
remove_wait_queue(&drivers[drvidx]->st_waitq, &wait); remove_wait_queue(&drv->st_waitq, &wait);
if (len < 0) if (len < 0)
return len; return len;
...@@ -1543,15 +1511,15 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off) ...@@ -1543,15 +1511,15 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off)
if (count > len) if (count > len)
count = len; count = len;
len = drivers[drvidx]->interface->readstat(buf, count, 1, drvidx, len = drv->interface->readstat(buf, count, 1, drv->di,
isdn_minor2chan(minor)); isdn_minor2chan(minor));
spin_lock_irqsave(&stat_lock, flags); spin_lock_irqsave(&stat_lock, flags);
if (len) { if (len) {
drivers[drvidx]->stavail -= len; drv->stavail -= len;
} else { } else {
isdn_BUG(); isdn_BUG();
drivers[drvidx]->stavail = 0; drv->stavail = 0;
} }
spin_unlock_irqrestore(&stat_lock, flags); spin_unlock_irqrestore(&stat_lock, flags);
...@@ -1562,24 +1530,20 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off) ...@@ -1562,24 +1530,20 @@ isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off)
static ssize_t static ssize_t
isdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off) isdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off)
{ {
uint minor = minor(file->f_dentry->d_inode->i_rdev); struct isdn_driver *drv = file->private_data;
int drvidx; unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);
int retval; int retval;
if (off != &file->f_pos) if (off != &file->f_pos)
return -ESPIPE; return -ESPIPE;
drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (!drv->interface->writecmd) {
if (drvidx < 0) {
retval = -ENODEV;
goto out;
}
if (!drivers[drvidx]->interface->writecmd) {
retval = -EINVAL; retval = -EINVAL;
goto out; goto out;
} }
retval = drivers[drvidx]->interface-> retval = drv->interface->
writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor - ISDN_MINOR_CTRL)); writecmd(buf, count, 1, drv->di,
isdn_minor2chan(minor - ISDN_MINOR_CTRL));
out: out:
return retval; return retval;
...@@ -1588,18 +1552,12 @@ isdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off) ...@@ -1588,18 +1552,12 @@ isdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off)
static unsigned int static unsigned int
isdn_ctrl_poll(struct file *file, poll_table *wait) isdn_ctrl_poll(struct file *file, poll_table *wait)
{ {
struct isdn_driver *drv = file->private_data;
unsigned int mask = 0; unsigned int mask = 0;
unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);
int drvidx;
drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
if (drvidx < 0)
/* driver deregistered while file open */
return POLLHUP;
poll_wait(file, &drivers[drvidx]->st_waitq, wait); poll_wait(file, &drv->st_waitq, wait);
mask = POLLOUT | POLLWRNORM; mask = POLLOUT | POLLWRNORM;
if (drivers[drvidx]->stavail) if (drv->stavail)
mask |= POLLIN | POLLRDNORM; mask |= POLLIN | POLLRDNORM;
return mask; return mask;
......
...@@ -9,8 +9,12 @@ ...@@ -9,8 +9,12 @@
#define ISDN_TTY_STAT_DEBUG #define ISDN_TTY_STAT_DEBUG
#define ISDN_DEBUG_MODEM_HUP #define ISDN_DEBUG_MODEM_HUP
#define ISDN_DEBUG_MODEM_VOICE
#define ISDN_DEBUG_MODEM_OPEN
#define ISDN_DEBUG_MODEM_IOCTL
#define ISDN_DEBUG_MODEM_ICALL
#include <linux/config.h> #include <linux/module.h>
#include <linux/isdn.h> #include <linux/isdn.h>
#include "isdn_common.h" #include "isdn_common.h"
#include "isdn_tty.h" #include "isdn_tty.h"
...@@ -1129,7 +1133,6 @@ isdn_tty_startup(modem_info * info) ...@@ -1129,7 +1133,6 @@ isdn_tty_startup(modem_info * info)
return 0; return 0;
save_flags(flags); save_flags(flags);
cli(); cli();
isdn_MOD_INC_USE_COUNT();
#ifdef ISDN_DEBUG_MODEM_OPEN #ifdef ISDN_DEBUG_MODEM_OPEN
printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line); printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
#endif #endif
...@@ -1167,7 +1170,6 @@ isdn_tty_shutdown(modem_info * info) ...@@ -1167,7 +1170,6 @@ isdn_tty_shutdown(modem_info * info)
#endif #endif
save_flags(flags); save_flags(flags);
cli(); /* Disable interrupts */ cli(); /* Disable interrupts */
isdn_MOD_DEC_USE_COUNT();
info->msr &= ~UART_MSR_RI; info->msr &= ~UART_MSR_RI;
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
...@@ -1758,8 +1760,11 @@ static int ...@@ -1758,8 +1760,11 @@ static int
isdn_tty_open(struct tty_struct *tty, struct file *filp) isdn_tty_open(struct tty_struct *tty, struct file *filp)
{ {
modem_info *info; modem_info *info;
int retval, int retval, line;
line;
/* FIXME. This is not unload-race free AFAICS */
MOD_INC_USE_COUNT;
line = minor(tty->device) - tty->driver.minor_start; line = minor(tty->device) - tty->driver.minor_start;
if (line < 0 || line > ISDN_MAX_CHANNELS) if (line < 0 || line > ISDN_MAX_CHANNELS)
...@@ -1818,7 +1823,8 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) ...@@ -1818,7 +1823,8 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
ulong timeout; ulong timeout;
if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close")) if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close"))
return; goto out;
save_flags(flags); save_flags(flags);
cli(); cli();
if (tty_hung_up_p(filp)) { if (tty_hung_up_p(filp)) {
...@@ -1826,7 +1832,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) ...@@ -1826,7 +1832,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
#ifdef ISDN_DEBUG_MODEM_OPEN #ifdef ISDN_DEBUG_MODEM_OPEN
printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n"); printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
#endif #endif
return; goto out;
} }
if ((tty->count == 1) && (info->count != 1)) { if ((tty->count == 1) && (info->count != 1)) {
/* /*
...@@ -1850,7 +1856,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) ...@@ -1850,7 +1856,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
#ifdef ISDN_DEBUG_MODEM_OPEN #ifdef ISDN_DEBUG_MODEM_OPEN
printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n"); printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
#endif #endif
return; goto out;
} }
info->flags |= ISDN_ASYNC_CLOSING; info->flags |= ISDN_ASYNC_CLOSING;
/* /*
...@@ -1905,6 +1911,8 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) ...@@ -1905,6 +1911,8 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
#ifdef ISDN_DEBUG_MODEM_OPEN #ifdef ISDN_DEBUG_MODEM_OPEN
printk(KERN_DEBUG "isdn_tty_close normal exit\n"); printk(KERN_DEBUG "isdn_tty_close normal exit\n");
#endif #endif
out:
MOD_DEC_USE_COUNT;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment