summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi_dynamic.c
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2010-02-25 19:10:02 +0000
committerShaun Ruffell <sruffell@digium.com>2010-02-25 19:10:02 +0000
commit9e42acddf0f25ca9c17bf370bb008be0799fc44f (patch)
treea8b8b40eb01eee47fe29583af71edd2ebe73ceac /drivers/dahdi/dahdi_dynamic.c
parentfeef9550b093994d23a6e3eecd96db1708be408d (diff)
dahdi_dynamic: Add TDMoE Multi-Frame support.
Add TDMoE Multi-Frame support as described in the article at the following URL: http://www.thrallingpenguin.com/articles/tdmoe-mf.htm TDMoE-MF is known to be implemented in hardware solutions from Redfone Communications. This patch additionally implements RCU within dahdi_dynamic to decrease lock contention, latency, and context switching. Because of the use of RCU locking, all prior known issues with loading and unloading of the modules are resolved, providing the spans are shutdown with "dahdi_cfg -s". It also contains an attempt, which works, at fixing a kernel change with skb_linearize(). The use of kernel version number does not work with SuSE SLES 10, as it appears they have backported the 2.6.18 change in to their 2.6.16 version. This merges in the work Jbenden did at: http://svn.digium.com/svn/dahdi/team/jbenden/tdmoe-mf@8102 (issue #13483) Patch by: JBenden Reported by: JBenden Tested by: JBenden git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@8103 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/dahdi_dynamic.c')
-rw-r--r--drivers/dahdi/dahdi_dynamic.c286
1 files changed, 119 insertions, 167 deletions
diff --git a/drivers/dahdi/dahdi_dynamic.c b/drivers/dahdi/dahdi_dynamic.c
index e83fb80..e499344 100644
--- a/drivers/dahdi/dahdi_dynamic.c
+++ b/drivers/dahdi/dahdi_dynamic.c
@@ -31,7 +31,6 @@
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
-#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
#include <dahdi/kernel.h>
@@ -70,16 +69,19 @@
#define ZTD_FLAG_YELLOW_ALARM (1 << 0)
#define ZTD_FLAG_SIGBITS_PRESENT (1 << 1)
-#define ZTD_FLAG_LOOPBACK (1 << 2)
+#define ZTD_FLAG_LOOPBACK (1 << 2)
-#define ERR_NSAMP (1 << 16)
-#define ERR_NCHAN (1 << 17)
-#define ERR_LEN (1 << 18)
+#define ERR_NSAMP (1 << 16)
+#define ERR_NCHAN (1 << 17)
+#define ERR_LEN (1 << 18)
EXPORT_SYMBOL(dahdi_dynamic_register);
EXPORT_SYMBOL(dahdi_dynamic_unregister);
EXPORT_SYMBOL(dahdi_dynamic_receive);
+static int ztdynamic_init(void);
+static void ztdynamic_cleanup(void);
+
#ifdef ENABLE_TASKLETS
static int taskletrun;
static int taskletsched;
@@ -91,8 +93,7 @@ static struct tasklet_struct ztd_tlet;
static void ztd_tasklet(unsigned long data);
#endif
-
-static struct dahdi_dynamic {
+struct dahdi_dynamic {
char addr[40];
char dname[20];
int err;
@@ -103,40 +104,34 @@ static struct dahdi_dynamic {
unsigned short rxcnt;
struct dahdi_span span;
struct dahdi_chan *chans[DAHDI_DYNAMIC_MAX_CHANS];
- struct dahdi_dynamic *next;
struct dahdi_dynamic_driver *driver;
void *pvt;
int timing;
int master;
unsigned char *msgbuf;
-} *dspans;
-static struct dahdi_dynamic_driver *drivers = NULL;
+ struct list_head list;
+};
+
+static DEFINE_SPINLOCK(dspan_lock);
+static LIST_HEAD(dspan_list);
+
+static DEFINE_SPINLOCK(driver_lock);
+static LIST_HEAD(driver_list);
static int debug = 0;
static int hasmaster = 0;
-#ifdef DEFINE_SPINLOCK
-static DEFINE_SPINLOCK(dlock);
-#else
-static spinlock_t dlock = SPIN_LOCK_UNLOCKED;
-#endif
-
-#ifdef DEFINE_RWLOCK
-static DEFINE_RWLOCK(drvlock);
-#else
-static rwlock_t drvlock = RW_LOCK_UNLOCKED;
-#endif
static void checkmaster(void)
{
- unsigned long flags;
int newhasmaster=0;
int best = 9999999;
struct dahdi_dynamic *z, *master=NULL;
- spin_lock_irqsave(&dlock, flags);
- z = dspans;
- while(z) {
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(z, &dspan_list, list) {
if (z->timing) {
z->master = 0;
if (!(z->span.alarms & DAHDI_ALARM_RED) &&
@@ -148,13 +143,15 @@ static void checkmaster(void)
newhasmaster = 1;
}
}
- z = z->next;
}
+
hasmaster = newhasmaster;
/* Mark the new master if there is one */
if (master)
master->master = 1;
- spin_unlock_irqrestore(&dlock, flags);
+
+ rcu_read_unlock();
+
if (master)
printk(KERN_INFO "TDMoX: New master: %s\n", master->span.name);
else
@@ -223,15 +220,13 @@ static void ztd_sendmessage(struct dahdi_dynamic *z)
static void __ztdynamic_run(void)
{
- unsigned long flags;
struct dahdi_dynamic *z;
struct dahdi_dynamic_driver *drv;
int y;
- spin_lock_irqsave(&dlock, flags);
- z = dspans;
- while(z) {
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(z, &dspan_list, list) {
if (!z->dead) {
- /* Ignore dead spans */
for (y=0;y<z->span.channels;y++) {
/* Echo cancel double buffered data */
dahdi_ec_chunk(z->span.chans[y], z->span.chans[y]->readchunk, z->span.chans[y]->writechunk);
@@ -239,30 +234,23 @@ static void __ztdynamic_run(void)
dahdi_receive(&z->span);
dahdi_transmit(&z->span);
/* Handle all transmissions now */
- spin_unlock_irqrestore(&dlock, flags);
ztd_sendmessage(z);
- spin_lock_irqsave(&dlock, flags);
}
- z = z->next;
}
- spin_unlock_irqrestore(&dlock, flags);
- read_lock(&drvlock);
- drv = drivers;
- while(drv) {
+ list_for_each_entry_rcu(drv, &driver_list, list) {
/* Flush any traffic still pending in the driver */
if (drv->flush) {
drv->flush();
}
- drv = drv->next;
}
- read_unlock(&drvlock);
+ rcu_read_unlock();
}
#ifdef ENABLE_TASKLETS
static void ztdynamic_run(void)
{
- if (!taskletpending) {
+ if (likely(!taskletpending)) {
taskletpending = 1;
taskletsched++;
tasklet_hi_schedule(&ztd_tlet);
@@ -278,18 +266,17 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
{
struct dahdi_dynamic *ztd = span->pvt;
int newerr=0;
- unsigned long flags;
int sflags;
int xlen;
int x, bits, sig;
int nchans, master;
int newalarm;
unsigned short rxpos, rxcnt;
-
-
- spin_lock_irqsave(&dlock, flags);
- if (msglen < 6) {
- spin_unlock_irqrestore(&dlock, flags);
+
+ rcu_read_lock();
+
+ if (unlikely(msglen < 6)) {
+ rcu_read_unlock();
newerr = ERR_LEN;
if (newerr != ztd->err) {
printk(KERN_NOTICE "Span %s: Insufficient samples for header (only %d)\n", span->name, msglen);
@@ -299,8 +286,8 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
}
/* First, check the chunksize */
- if (*msg != DAHDI_CHUNKSIZE) {
- spin_unlock_irqrestore(&dlock, flags);
+ if (unlikely(*msg != DAHDI_CHUNKSIZE)) {
+ rcu_read_unlock();
newerr = ERR_NSAMP | msg[0];
if (newerr != ztd->err) {
printk(KERN_NOTICE "Span %s: Expected %d samples, but receiving %d\n", span->name, DAHDI_CHUNKSIZE, msg[0]);
@@ -311,14 +298,14 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
msg++;
sflags = *msg;
msg++;
-
+
rxpos = ntohs(*((unsigned short *)msg));
msg++;
msg++;
-
+
nchans = ntohs(*((unsigned short *)msg));
- if (nchans != span->channels) {
- spin_unlock_irqrestore(&dlock, flags);
+ if (unlikely(nchans != span->channels)) {
+ rcu_read_unlock();
newerr = ERR_NCHAN | nchans;
if (newerr != ztd->err) {
printk(KERN_NOTICE "Span %s: Expected %d channels, but receiving %d\n", span->name, span->channels, nchans);
@@ -328,7 +315,7 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
}
msg++;
msg++;
-
+
/* Okay now we've accepted the header, lets check our message
length... */
@@ -341,9 +328,9 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
/* Account for sigbits -- one short per 4 channels*/
xlen += ((nchans + 3) / 4) * 2;
}
-
- if (xlen != msglen) {
- spin_unlock_irqrestore(&dlock, flags);
+
+ if (unlikely(xlen != msglen)) {
+ rcu_read_unlock();
newerr = ERR_LEN | xlen;
if (newerr != ztd->err) {
printk(KERN_NOTICE "Span %s: Expected message size %d, but was %d instead\n", span->name, xlen, msglen);
@@ -351,9 +338,9 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
ztd->err = newerr;
return;
}
-
+
bits = 0;
-
+
/* Record sigbits if present */
if (sflags & ZTD_FLAG_SIGBITS_PRESENT) {
for (x=0;x<nchans;x++) {
@@ -385,8 +372,11 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
rxcnt = ztd->rxcnt;
ztd->rxcnt = rxpos+1;
- spin_unlock_irqrestore(&dlock, flags);
-
+ /* Keep track of last received packet */
+ ztd->rxjif = jiffies;
+
+ rcu_read_unlock();
+
/* Check for Yellow alarm */
newalarm = span->alarms & ~(DAHDI_ALARM_YELLOW | DAHDI_ALARM_RED);
if (sflags & ZTD_FLAG_YELLOW_ALARM)
@@ -397,18 +387,14 @@ void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msgl
dahdi_alarm_notify(span);
checkmaster();
}
-
- /* Keep track of last received packet */
- ztd->rxjif = jiffies;
/* note if we had a missing packet */
- if (rxpos != rxcnt)
+ if (unlikely(rxpos != rxcnt))
printk(KERN_NOTICE "Span %s: Expected seq no %d, but received %d instead\n", span->name, rxcnt, rxpos);
/* If this is our master span, then run everything */
if (master)
ztdynamic_run();
-
}
static void dynamic_destroy(struct dahdi_dynamic *z)
@@ -440,64 +426,61 @@ static void dynamic_destroy(struct dahdi_dynamic *z)
static struct dahdi_dynamic *find_dynamic(struct dahdi_dynamic_span *zds)
{
- struct dahdi_dynamic *z;
- z = dspans;
- while(z) {
+ struct dahdi_dynamic *z = NULL, *found = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(z, &dspan_list, list) {
if (!strcmp(z->dname, zds->driver) &&
- !strcmp(z->addr, zds->addr))
+ !strcmp(z->addr, zds->addr)) {
+ found = z;
break;
- z = z->next;
+ }
}
- return z;
+ rcu_read_unlock();
+
+ return found;
}
static struct dahdi_dynamic_driver *find_driver(char *name)
{
- struct dahdi_dynamic_driver *ztd;
- ztd = drivers;
- while(ztd) {
+ struct dahdi_dynamic_driver *ztd, *found = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ztd, &driver_list, list) {
/* here's our driver */
- if (!strcmp(name, ztd->name))
+ if (!strcmp(name, ztd->name)) {
+ found = ztd;
break;
- ztd = ztd->next;
+ }
}
- return ztd;
+ rcu_read_unlock();
+
+ return found;
}
static int destroy_dynamic(struct dahdi_dynamic_span *zds)
{
unsigned long flags;
- struct dahdi_dynamic *z, *cur, *prev=NULL;
- spin_lock_irqsave(&dlock, flags);
+ struct dahdi_dynamic *z;
+
z = find_dynamic(zds);
- if (!z) {
- spin_unlock_irqrestore(&dlock, flags);
+ if (unlikely(!z)) {
return -EINVAL;
}
- /* Don't destroy span until it is in use */
+
if (z->usecount) {
- spin_unlock_irqrestore(&dlock, flags);
printk(KERN_NOTICE "Attempt to destroy dynamic span while it is in use\n");
return -EBUSY;
}
- /* Unlink it */
- cur = dspans;
- while(cur) {
- if (cur == z) {
- if (prev)
- prev->next = z->next;
- else
- dspans = z->next;
- break;
- }
- prev = cur;
- cur = cur->next;
- }
- spin_unlock_irqrestore(&dlock, flags);
+
+ spin_lock_irqsave(&dspan_lock, flags);
+ list_del_rcu(&z->list);
+ spin_unlock_irqrestore(&dspan_lock, flags);
+ synchronize_rcu();
/* Destroy it */
dynamic_destroy(z);
-
+
return 0;
}
@@ -511,8 +494,8 @@ static int ztd_open(struct dahdi_chan *chan)
{
struct dahdi_dynamic *z;
z = chan->span->pvt;
- if (z) {
- if (z->dead)
+ if (likely(z)) {
+ if (unlikely(z->dead))
return -ENODEV;
z->usecount++;
}
@@ -528,10 +511,11 @@ static int ztd_close(struct dahdi_chan *chan)
{
struct dahdi_dynamic *z;
z = chan->span->pvt;
- if (z)
+ if (z) {
z->usecount--;
- if (z->dead && !z->usecount)
- dynamic_destroy(z);
+ if (z->dead && !z->usecount)
+ dynamic_destroy(z);
+ }
return 0;
}
@@ -552,21 +536,13 @@ static int create_dynamic(struct dahdi_dynamic_span *zds)
return -EINVAL;
}
- spin_lock_irqsave(&dlock, flags);
z = find_dynamic(zds);
- spin_unlock_irqrestore(&dlock, flags);
if (z)
return -EEXIST;
- /* XXX There is a silly race here. We check it doesn't exist, but
- someone could create it between now and then and we'd end up
- with two of them. We don't want to hold the spinlock
- for *too* long though, especially not if there is a possibility
- of kmalloc. XXX */
-
-
/* Allocate memory */
- if (!(z = kmalloc(sizeof(*z), GFP_KERNEL))) {
+ z = (struct dahdi_dynamic *) kmalloc(sizeof(struct dahdi_dynamic), GFP_KERNEL);
+ if (!z) {
return -ENOMEM;
}
@@ -621,24 +597,20 @@ static int create_dynamic(struct dahdi_dynamic_span *zds)
z->chans[x]->pvt = z;
}
- spin_lock_irqsave(&dlock, flags);
ztd = find_driver(zds->driver);
if (!ztd) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
char fn[80];
#endif
- spin_unlock_irqrestore(&dlock, flags);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70)
request_module("dahdi_dynamic_%s", zds->driver);
#else
sprintf(fn, "dahdi_dynamic_%s", zds->driver);
request_module(fn);
#endif
- spin_lock_irqsave(&dlock, flags);
ztd = find_driver(zds->driver);
}
- spin_unlock_irqrestore(&dlock, flags);
/* Another race -- should let the module get unloaded while we
@@ -667,11 +639,9 @@ static int create_dynamic(struct dahdi_dynamic_span *zds)
return -EINVAL;
}
- /* Okay, created and registered. add it to the list */
- spin_lock_irqsave(&dlock, flags);
- z->next = dspans;
- dspans = z;
- spin_unlock_irqrestore(&dlock, flags);
+ spin_lock_irqsave(&dspan_lock, flags);
+ list_add_rcu(&z->list, &dspan_list);
+ spin_unlock_irqrestore(&dspan_lock, flags);
checkmaster();
@@ -732,70 +702,52 @@ int dahdi_dynamic_register(struct dahdi_dynamic_driver *dri)
{
unsigned long flags;
int res = 0;
- write_lock_irqsave(&drvlock, flags);
- if (find_driver(dri->name))
+
+ if (find_driver(dri->name)) {
res = -1;
- else {
- dri->next = drivers;
- drivers = dri;
+ } else {
+ spin_lock_irqsave(&driver_lock, flags);
+ list_add_rcu(&dri->list, &driver_list);
+ spin_unlock_irqrestore(&driver_lock, flags);
}
- write_unlock_irqrestore(&drvlock, flags);
return res;
}
void dahdi_dynamic_unregister(struct dahdi_dynamic_driver *dri)
{
- struct dahdi_dynamic_driver *cur, *prev=NULL;
- struct dahdi_dynamic *z, *zp, *zn;
+ struct dahdi_dynamic *z;
unsigned long flags;
- write_lock_irqsave(&drvlock, flags);
- cur = drivers;
- while(cur) {
- if (cur == dri) {
- if (prev)
- prev->next = cur->next;
- else
- drivers = cur->next;
- break;
- }
- prev = cur;
- cur = cur->next;
- }
- write_unlock_irqrestore(&drvlock, flags);
- spin_lock_irqsave(&dlock, flags);
- z = dspans;
- zp = NULL;
- while(z) {
- zn = z->next;
+
+ spin_lock_irqsave(&driver_lock, flags);
+ list_del_rcu(&dri->list);
+ spin_unlock_irqrestore(&driver_lock, flags);
+ synchronize_rcu();
+
+ list_for_each_entry(z, &dspan_list, list) {
if (z->driver == dri) {
- /* Unlink */
- if (zp)
- zp->next = z->next;
- else
- dspans = z->next;
+ spin_lock_irqsave(&dspan_lock, flags);
+ list_del_rcu(&z->list);
+ spin_unlock_irqrestore(&dspan_lock, flags);
+ synchronize_rcu();
+
if (!z->usecount)
dynamic_destroy(z);
else
z->dead = 1;
- } else {
- zp = z;
}
- z = zn;
}
- spin_unlock_irqrestore(&dlock, flags);
}
static struct timer_list alarmcheck;
static void check_for_red_alarm(unsigned long ignored)
{
- unsigned long flags;
int newalarm;
int alarmchanged = 0;
struct dahdi_dynamic *z;
- spin_lock_irqsave(&dlock, flags);
- z = dspans;
- while(z) {
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(z, &dspan_list, list) {
newalarm = z->span.alarms & ~DAHDI_ALARM_RED;
/* If nothing received for a second, consider that RED ALARM */
if ((jiffies - z->rxjif) > 1 * HZ) {
@@ -806,20 +758,20 @@ static void check_for_red_alarm(unsigned long ignored)
alarmchanged++;
}
}
- z = z->next;
}
- spin_unlock_irqrestore(&dlock, flags);
+ rcu_read_unlock();
+
if (alarmchanged)
checkmaster();
/* Do the next one */
mod_timer(&alarmcheck, jiffies + 1 * HZ);
-
}
static int ztdynamic_init(void)
{
dahdi_set_dynamic_ioctl(ztdynamic_ioctl);
+
/* Start process to check for RED ALARM */
init_timer(&alarmcheck);
alarmcheck.expires = 0;