--- dahdi-linux-complete-2.7.0.1+2.7.0.1/linux/drivers/dahdi/dahdi_dynamic.c 2013-08-22 02:36:34.000000000 +0700 +++ dahdi-linux-complete/linux/drivers/dahdi/dahdi_dynamic.c 2013-11-07 23:45:46.302897998 +0700 @@ -22,6 +22,12 @@ * this program for more details. */ +/* + 2009.09.19, pavel[AT]parabel.ru + Implemented buffering on rx. + Implemented procfs statistics. + */ + #include #include #include @@ -32,6 +38,9 @@ #include #include #include +#include +#include +#include #include @@ -47,7 +56,7 @@ * tasklets. */ -#undef ENABLE_TASKLETS +#define ENABLE_TASKLETS /* * Dynamic spans implemented using TDM over X with standard message @@ -92,6 +101,33 @@ static struct tasklet_struct dahdi_dynamic_flush_tlet; #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) +# define kfifo_in(fifo,buf,n) __kfifo_put(fifo,buf,n) +# define kfifo_in_spinlocked(fifo,buf,n,lock) kfifo_put(fifo,buf,n) +# define kfifo_out(fifo,buf,n) __kfifo_get(fifo,buf,n) +# define kfifo_out_spinlocked(fifo,buf,n,lock) kfifo_get(fifo,buf,n) +# define kfifo_size(fifo) (fifo->size) +# define my_kfifo_alloc(size,mask,lock) kfifo_alloc(size,mask,lock) +# define my_kfifo_free(fifo) kfifo_free(fifo) +#else // >= 2.6.35 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) /* 2.6.35 <= x < 2.6.36 */ +#define kfifo_in_spinlocked(fifo, buf, n, lock) \ + kfifo_in_locked(fifo, buf, n, lock) +#define kfifo_out_spinlocked(fifo, buf, n, lock) \ + kfifo_out_locked(fifo, buf, n, lock) +#endif // < KERNEL_VERSION(2,6,36) +inline struct kfifo *my_kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock) { + int ret; + struct kfifo *fifo = kmalloc(sizeof(struct kfifo), gfp_mask); + ret = kfifo_alloc (fifo, size, gfp_mask); + return fifo; +} +inline void my_kfifo_free(struct kfifo *fifo) { + kfifo_free (fifo); + kfree (fifo); +} +#endif // < KERNEL_VERSION(2,6,35) + static DEFINE_MUTEX(dspan_mutex); static DEFINE_SPINLOCK(dspan_lock); static DEFINE_SPINLOCK(driver_lock); @@ -100,8 +136,11 @@ static LIST_HEAD(driver_list); static int debug = 0; - +static int rxfifo = 4; static int hasmaster = 0; +static int dynamic_master_tick; /* ticks from dynamic master SPAN. */ +static int dahdi_sync_tick; /* ticks from DAHDI to dynamic master SPAN */ +static int dahdi_in_sync; /* Is DAHDI in sync with dahdi_dynamic ? */ static void checkmaster(void) { @@ -112,8 +151,10 @@ rcu_read_lock(); list_for_each_entry_rcu(d, &dspan_list, list) { + d->span.cannot_provide_timing = 1; if (d->timing) { d->master = 0; + d->span.syncsrc = 0; if (!(d->span.alarms & DAHDI_ALARM_RED) && (d->timing < best)) { /* If not in alarm and they're @@ -127,8 +168,15 @@ hasmaster = newhasmaster; /* Mark the new master if there is one */ - if (master) + if (master) { + /* Marking dynamic master SPAN with cannot_provide_timing = 0. + Thus, DAHDI will take this SPAN as a master for DAHDI. + In DAHDI < 2.6.0, We had to do the same with DAHDI_FLAG_RUNNING. */ + master->span.cannot_provide_timing = 0; + master->span.syncsrc = master->span.spanno; master->master = 1; + dahdi_alarm_notify(&(master->span)); + } rcu_read_unlock(); @@ -198,6 +246,38 @@ } +static void dahdi_dynamic_process_rxfifo (struct dahdi_dynamic *d) { + unsigned char *msg = d->msgbuf; + int x = d->span.channels * DAHDI_CHUNKSIZE; + + if (kfifo_out_spinlocked(d->rxfifo, msg, x, &d->rxfifo_lock) != x) { + ++d->slip; + return; + } + + for (x=0;xspan.channels;x++) { + memcpy(d->span.chans[x]->readchunk, msg, DAHDI_CHUNKSIZE); + msg += DAHDI_CHUNKSIZE; + } +} + +static void _dahdi_dynamic_clearstat (void) { + struct dahdi_dynamic *d; + + dynamic_master_tick = dahdi_sync_tick = 0; + dahdi_in_sync = 2; +#ifdef ENABLE_TASKLETS + taskletrun = taskletsched = taskletexec = txerrors = taskletpending = 0; +#endif + + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + d->slip = d->skip = d->rxnuerr = 0; + } + rcu_read_unlock(); + +} + static void __dahdi_dynamic_run(void) { struct dahdi_dynamic *d; @@ -205,12 +285,15 @@ rcu_read_lock(); list_for_each_entry_rcu(d, &dspan_list, list) { + dahdi_dynamic_process_rxfifo (d); + dahdi_ec_span(&d->span); + dahdi_receive(&d->span); dahdi_transmit(&d->span); /* Handle all transmissions now */ dahdi_dynamic_sendmessage(d); } -#ifdef ENABLE_TASKLETS +#ifndef ENABLE_TASKLETS /* If tasklets are not enabled, the above section will be called in * interrupt context and the flushing of each driver will be called in a * separate tasklet that only handles that. This is necessary since some @@ -348,10 +431,12 @@ } /* Record data for channels */ - for (x=0;xchans[x]->readchunk, msg, DAHDI_CHUNKSIZE); - msg += DAHDI_CHUNKSIZE; - } + x = nchans*DAHDI_CHUNKSIZE; + /* Make sure we have enougth space in fifo for the whole packet. */ + if (dtd->rxfifo_size >= kfifo_len (dtd->rxfifo) + x) + kfifo_in_spinlocked(dtd->rxfifo, msg, x, &dtd->rxfifo_lock); + else + ++dtd->skip; master = dtd->master; @@ -370,20 +455,23 @@ if (newalarm != span->alarms) { span->alarms = newalarm; - dahdi_alarm_notify(span); checkmaster(); + dahdi_alarm_notify(span); } /* note if we had a missing packet */ - if (unlikely(rxpos != rxcnt)) - printk(KERN_NOTICE "Span %s: Expected seq no %d, but received %d instead\n", span->name, rxcnt, rxpos); - - dahdi_ec_span(span); - dahdi_receive(span); + if (unlikely(rxpos != rxcnt)) { + ++dtd->rxnuerr; + if (debug) + 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) + if (master) { + ++dynamic_master_tick; dahdi_dynamic_run(); + } } EXPORT_SYMBOL(dahdi_dynamic_receive); @@ -405,6 +493,12 @@ for (x = 0; x < d->span.channels; x++) kfree(d->chans[x]); + /* Free rxfifo */ + if (d->rxfifo) { + my_kfifo_free (d->rxfifo); + d->rxfifo = NULL; + } + dahdi_free_device(d->ddev); kfree(d); } @@ -546,6 +640,11 @@ struct dahdi_dynamic *head; struct dahdi_dynamic *d = dynamic_from_span(span); + if (is_master) { /* Some dynamic SPAN is a (MASTER) for DAHDI. */ + ++dahdi_sync_tick; + dahdi_in_sync = 2; + } + if (hasmaster) return; @@ -576,6 +675,9 @@ int x; int bufsize; + if (list_empty_careful(&dspan_list)) + _dahdi_dynamic_clearstat(); + if (dds->numchans < 1) { printk(KERN_NOTICE "Can't be less than 1 channel (%d)!\n", dds->numchans); @@ -619,6 +721,24 @@ return -ENOMEM; } + /* We'd like to allocate rxfifo_size bytes FIFO */ + d->rxfifo_size = (dds->numchans * DAHDI_CHUNKSIZE) * rxfifo; + /* kfifo can handle FIFO size equal to power of 2. + kfifo_alloc >= 2.6.35 is buggy, let's round-up here */ + bufsize = roundup_pow_of_two(d->rxfifo_size); + /* TODO: Do we need FIFO locking ? We have one reader & one writer. + kfifo insist, it's safe. */ + spin_lock_init(&d->rxfifo_lock); + d->rxfifo = my_kfifo_alloc(bufsize, GFP_KERNEL, &d->rxfifo_lock); + if (!d->rxfifo) { + dynamic_put(d); + return -ENOMEM; + } + if (debug) + printk(KERN_INFO "Allocated kfifo. Requested %d, allocated %d\n", + d->rxfifo_size, kfifo_size(d->rxfifo)); + d->rxfifo_size=min(d->rxfifo_size, ((int)kfifo_size(d->rxfifo))); + /* Setup parameters properly assuming we're going to be okay. */ strlcpy(d->dname, dds->driver, sizeof(d->dname)); strlcpy(d->addr, dds->addr, sizeof(d->addr)); @@ -679,6 +799,8 @@ d->ddev->devicetype = d->span.name; d->ddev->hardware_id = d->span.name; + d->span.alarms |= DAHDI_ALARM_RED; /* We don't know our state yet... */ + d->span.flags |= DAHDI_FLAG_RUNNING; dev_set_name(&d->ddev->dev, "dynamic:%s:%d", dds->driver, dtd->id++); list_add_tail(&d->span.device_node, &d->ddev->spans); /* Whee! We're created. Now register the span */ @@ -695,10 +817,11 @@ /* Transfer our reference to the dspan_list. Do not touch d after * this point. It also must remain on the list while registered. */ spin_lock_irqsave(&dspan_lock, flags); - list_add_rcu(&d->list, &dspan_list); + list_add_tail_rcu(&d->list, &dspan_list); spin_unlock_irqrestore(&dspan_lock, flags); - checkmaster(); + /* First incoming packet will clear red alarm, so, checkmaster will be called in a proper time. */ + dahdi_alarm_notify(&d->span); module_put(dtd->owner); return x; @@ -782,7 +905,7 @@ res = -1; } else { spin_lock_irqsave(&driver_lock, flags); - list_add_rcu(&dri->list, &driver_list); + list_add_tail_rcu(&dri->list, &driver_list); spin_unlock_irqrestore(&driver_lock, flags); } return res; @@ -839,6 +962,7 @@ newalarm |= DAHDI_ALARM_RED; if (d->span.alarms != newalarm) { d->span.alarms = newalarm; + checkmaster(); dahdi_alarm_notify(&d->span); alarmchanged++; } @@ -849,10 +973,111 @@ if (alarmchanged) checkmaster(); + if (hasmaster) { + --dahdi_in_sync; + if (dahdi_in_sync <= 0) + dahdi_in_sync = 0; + if (debug && !dahdi_in_sync) + printk(KERN_NOTICE "TDMoX: Seems like dynamic is working " \ + "out of sync with DAHDI. Check you configuration. \n"); + } + /* Do the next one */ mod_timer(&alarmcheck, jiffies + 1 * HZ); } +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_entry = NULL; +static const char *dahdi_dynamic_procname="dahdi/dahdi_dynamic_stats"; + +static int dahdi_dynamic_show(struct seq_file *sfile, void *data) +{ + struct dahdi_dynamic *d; + + seq_printf(sfile, "dahdi_dynamic info/statistics\n"); + +#ifdef ENABLE_TASKLETS + seq_printf(sfile, "\ttaskletrun: %d\n", taskletrun); + seq_printf(sfile, "\ttaskletsched: %d\n", taskletsched); + seq_printf(sfile, "\ttaskletexec: %d\n", taskletexec); + seq_printf(sfile, "\ttxerrors: %d\n", txerrors); + seq_printf(sfile, "\ttaskletpending: %d\n", taskletpending); + seq_printf(sfile, "\n"); +#endif + seq_printf(sfile, "\tdynamic_master_tick: %d\n", dynamic_master_tick); + seq_printf(sfile, "\tdahdi_sync_tick: %d\n", dahdi_sync_tick); + seq_printf(sfile, "\tdahdi_in_sync: %s\n", dahdi_in_sync ? "yes" : "NO"); + + seq_printf(sfile, "\n"); + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + struct dahdi_span *span = &(d->span); + if (span->name) + seq_printf(sfile, "Span %d: %s ", span->spanno, span->name); + if (span->desc) + seq_printf(sfile, "\"%s\"", span->desc); + else + seq_printf(sfile, "\"\""); + + seq_printf(sfile, " "); + if (d->master) + seq_printf(sfile, "ClockSource "); + seq_printf(sfile, "\n"); + + seq_printf(sfile, "\tslip: %d", d->slip); + seq_printf(sfile, ", skip: %d", d->skip); + seq_printf(sfile, ", rxnuerr: %d", d->rxnuerr); + seq_printf(sfile, ", rxfifo: %d", kfifo_len(d->rxfifo)/span->channels/DAHDI_CHUNKSIZE); + seq_printf(sfile, "\n"); + } + rcu_read_unlock(); + + return 0; +} + +static int dahdi_dynamic_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dahdi_dynamic_show, PDE(inode)->data); +} + +static int dahdi_dynamic_proc_write(struct file *file, const char __user *usrbuf, + size_t count, loff_t *ppos) +{ + _dahdi_dynamic_clearstat(); + return count; +} + +static const struct file_operations dahdi_dynamic_proc_ops = { + .owner = THIS_MODULE, + .open = dahdi_dynamic_proc_open, + .read = seq_read, + .write = dahdi_dynamic_proc_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void proc_init (void) +{ + proc_entry = create_proc_entry(dahdi_dynamic_procname, 0444, + NULL); + if (!proc_entry) { + /* TODO */ + return; + } + proc_entry->data = NULL; + proc_entry->proc_fops = &dahdi_dynamic_proc_ops; +} + +static void proc_cleanup (void) +{ + if (proc_entry) + remove_proc_entry(dahdi_dynamic_procname, NULL); +} +#else +# define proc_init() +# define proc_cleanup() +#endif //CONFIG_PROC_FS + static const struct dahdi_dynamic_ops dahdi_dynamic_ops = { .owner = THIS_MODULE, .ioctl = dahdi_dynamic_ioctl, @@ -860,6 +1085,8 @@ static int dahdi_dynamic_init(void) { + rxfifo = max(rxfifo, 1); + proc_init (); /* Start process to check for RED ALARM */ init_timer(&alarmcheck); alarmcheck.expires = 0; @@ -874,7 +1101,7 @@ #endif dahdi_set_dynamic_ops(&dahdi_dynamic_ops); - printk(KERN_INFO "DAHDI Dynamic Span support LOADED\n"); + printk(KERN_INFO "DAHDI Dynamic Span support LOADED, rxfifo=%d\n", rxfifo); return 0; } @@ -895,10 +1122,13 @@ /* Must call again in case it was running before and rescheduled * itself. */ del_timer(&alarmcheck); + proc_cleanup (); printk(KERN_INFO "DAHDI Dynamic Span support unloaded\n"); } module_param(debug, int, 0600); +module_param(rxfifo, int, 0600); +MODULE_PARM_DESC(rxfifo, "RX fifo size (milliseconds)."); MODULE_DESCRIPTION("DAHDI Dynamic Span Support"); MODULE_AUTHOR("Mark Spencer ");