summaryrefslogtreecommitdiff
path: root/drivers/dahdi/xpp
diff options
context:
space:
mode:
authorTzafrir Cohen <tzafrir.cohen@xorcom.com>2010-07-13 10:31:55 +0000
committerTzafrir Cohen <tzafrir.cohen@xorcom.com>2010-07-13 10:31:55 +0000
commit3d5c42a871d30c4fb6ee6520b0c4c39ffa86fe18 (patch)
tree412437bd554480cfeb9e265bd7c7dea576f56268 /drivers/dahdi/xpp
parentd6035ac6ad1c10d867c57cd53e074fa521a537b0 (diff)
Solve race xbus_populate
Fixes a crash resulting from a race between disconnecting and connecting Astribanks (on multi-core systems) * Use get_xbus()/put_xbus() arround xbus_populate(), so a disconnect in the middle won't release the xbus too early. * Aquire all XPDs before starting initialization and release them after it finishes (so we don't have up/down races among XPDs) git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@8886 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/xpp')
-rw-r--r--drivers/dahdi/xpp/card_global.c3
-rw-r--r--drivers/dahdi/xpp/xbus-core.c62
2 files changed, 54 insertions, 11 deletions
diff --git a/drivers/dahdi/xpp/card_global.c b/drivers/dahdi/xpp/card_global.c
index 2428cbe..4cf5ca5 100644
--- a/drivers/dahdi/xpp/card_global.c
+++ b/drivers/dahdi/xpp/card_global.c
@@ -799,7 +799,8 @@ int run_initialize_registers(xpd_t *xpd)
xbus = xpd->xbus;
if(!initdir || !initdir[0]) {
XPD_NOTICE(xpd, "Missing initdir parameter\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
if(!xpd_setstate(xpd, XPD_STATE_INIT_REGS)) {
ret = -EINVAL;
diff --git a/drivers/dahdi/xpp/xbus-core.c b/drivers/dahdi/xpp/xbus-core.c
index 79f417a..569b678 100644
--- a/drivers/dahdi/xpp/xbus-core.c
+++ b/drivers/dahdi/xpp/xbus-core.c
@@ -814,16 +814,45 @@ static void xbus_release_xpds(xbus_t *xbus)
{
int i;
- XBUS_INFO(xbus, "[%s] Release XPDS\n", xbus->label);
+ XBUS_DBG(DEVICES, xbus, "[%s] Release XPDS\n", xbus->label);
for(i = 0; i < MAX_XPDS; i++) {
xpd_t *xpd = xpd_of(xbus, i);
if(xpd)
- /* taken in xpd_device_register() */
put_xpd(__func__, xpd);
}
}
+static int xbus_aquire_xpds(xbus_t *xbus)
+{
+ unsigned long flags;
+ int i;
+ int ret = 0;
+ xpd_t *xpd;
+
+ XBUS_DBG(DEVICES, xbus, "[%s] Aquire XPDS\n", xbus->label);
+ spin_lock_irqsave(&xbus->lock, flags);
+ for (i = 0; i < MAX_XPDS; i++) {
+ xpd = xpd_of(xbus, i);
+ if (xpd) {
+ xpd = get_xpd(__func__, xpd);
+ if (!xpd)
+ goto err;
+ }
+ }
+out:
+ spin_unlock_irqrestore(&xbus->lock, flags);
+ return ret;
+err:
+ for (--i ; i >= 0; i--) {
+ xpd = xpd_of(xbus, i);
+ if (xpd)
+ put_xpd(__func__, xpd);
+ }
+ ret = -EBUSY;
+ goto out;
+}
+
static int xpd_initialize(xpd_t *xpd)
{
int ret = -ENODEV;
@@ -853,14 +882,24 @@ static int xbus_initialize(xbus_t *xbus)
struct timeval time_start;
struct timeval time_end;
unsigned long timediff;
+ int res = 0;
do_gettimeofday(&time_start);
XBUS_DBG(DEVICES, xbus, "refcount_xbus=%d\n",
refcount_xbus(xbus));
+ if (xbus_aquire_xpds(xbus) < 0) /* Until end of initialization */
+ return -EBUSY;
for(unit = 0; unit < MAX_UNIT; unit++) {
xpd = xpd_byaddr(xbus, unit, 0);
if(!xpd)
continue;
+ if (!XBUS_IS(xbus, RECVD_DESC)) {
+ XBUS_NOTICE(xbus,
+ "Cannot initialize UNIT=%d in state %s\n",
+ unit,
+ xbus_statename(XBUS_STATE(xbus)));
+ goto err;
+ }
if(run_initialize_registers(xpd) < 0) {
XBUS_ERR(xbus, "Register Initialization of card #%d failed\n", unit);
goto err;
@@ -871,15 +910,13 @@ static int xbus_initialize(xbus_t *xbus)
xpd = xpd_byaddr(xbus, unit, subunit);
if(!xpd)
continue;
- xpd = get_xpd(__FUNCTION__, xpd);
- if(!xpd) {
+ if (!XBUS_IS(xbus, RECVD_DESC)) {
XBUS_ERR(xbus,
- "Aborting initialization. XPD-%d%d is gone.\n",
- unit, subunit);
+ "XPD-%d%d Not in 'RECVD_DESC' state\n",
+ unit, subunit);
goto err;
}
ret = xpd_initialize(xpd);
- put_xpd(__FUNCTION__, xpd);
if(ret < 0)
goto err;
}
@@ -888,10 +925,13 @@ static int xbus_initialize(xbus_t *xbus)
timediff = usec_diff(&time_end, &time_start);
timediff /= 1000*100;
XBUS_INFO(xbus, "Initialized in %ld.%1ld sec\n", timediff/10, timediff%10);
- return 0;
+out:
+ xbus_release_xpds(xbus); /* Initialization done/failed */
+ return res;
err:
xbus_setstate(xbus, XBUS_STATE_FAIL);
- return -EINVAL;
+ res = -EINVAL;
+ goto out;
}
/*
@@ -915,6 +955,7 @@ void xbus_populate(void *data)
int ret = 0;
xbus = container_of(worker, xbus_t, worker);
+ xbus = get_xbus(__func__, xbus); /* return in function end */
XBUS_DBG(DEVICES, xbus, "Entering %s\n", __FUNCTION__);
spin_lock_irqsave(&worker->worker_lock, flags);
list_for_each_safe(card, next_card, &worker->card_list) {
@@ -961,6 +1002,7 @@ out:
wake_up_interruptible_all(&worker->wait_for_xpd_initialization);
XBUS_DBG(DEVICES, xbus, "populate release\n");
up(&worker->running_initialization);
+ put_xbus(__func__, xbus); /* taken at function entry */
return;
failed:
xbus_setstate(xbus, XBUS_STATE_FAIL);
@@ -1258,7 +1300,7 @@ void xbus_deactivate(xbus_t *xbus)
xbus_command_queue_waitempty(xbus);
xbus_setstate(xbus, XBUS_STATE_DEACTIVATED);
worker_reset(xbus);
- xbus_release_xpds(xbus);
+ xbus_release_xpds(xbus); /* taken in xpd_device_register() */
elect_syncer("deactivate");
}