--- 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-12-04 17:09:08.321228520 +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,8 +56,6 @@ * tasklets. */ -#undef ENABLE_TASKLETS - /* * Dynamic spans implemented using TDM over X with standard message * types. Message format is as follows: @@ -78,8 +85,15 @@ static int dahdi_dynamic_init(void); static void dahdi_dynamic_cleanup(void); +static void dahdi_dynamic_tasklet(unsigned long data); -#ifdef ENABLE_TASKLETS +/* +* As written at net/core/dev.c , interrupts MUST be enabled while calling dev_queue_xmit. +* Otherwise, we can get deadlock. +* As long, as sync_tick can be called in interrupt context (of another device/span), +* we MUST place our flush to tasklet/work/something else. +* So, it's not the questions, should we use tasklet or not. The questions is where. +*/ static int taskletrun; static int taskletsched; static int taskletpending; @@ -87,10 +101,32 @@ static int txerrors; static struct tasklet_struct dahdi_dynamic_tlet; -static void dahdi_dynamic_tasklet(unsigned long data); -#else -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); @@ -100,8 +136,13 @@ static LIST_HEAD(driver_list); static int debug = 0; - +static int rxfifo = 4; +static int rxtasklet = 0; +static int noflood = 1; 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 +153,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 +170,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(); @@ -195,46 +245,38 @@ } d->driver->transmit(d, d->msgbuf, msglen); - } -static void __dahdi_dynamic_run(void) -{ +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; - struct dahdi_dynamic_driver *drv; + + dynamic_master_tick = dahdi_sync_tick = 0; + dahdi_in_sync = 2; + taskletrun = taskletsched = taskletexec = txerrors = taskletpending = 0; rcu_read_lock(); list_for_each_entry_rcu(d, &dspan_list, list) { - dahdi_transmit(&d->span); - /* Handle all transmissions now */ - dahdi_dynamic_sendmessage(d); - } - -#ifdef 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 - * of the dynamic spans need to call functions that require interrupts - * to be enabled but dahdi_transmit / ...sendmessage needs to be called - * each time the masterspan is processed which happens with interrupts - * disabled. - * - */ - tasklet_hi_schedule(&dahdi_dynamic_flush_tlet); -#else - list_for_each_entry_rcu(drv, &driver_list, list) { - /* Flush any traffic still pending in the driver */ - if (drv->flush) { - drv->flush(); - } + d->slip = d->skip = d->rxnuerr = 0; } -#endif rcu_read_unlock(); } -#ifdef ENABLE_TASKLETS -static void dahdi_dynamic_run(void) -{ +static void dahdi_dynamic_start_tasklet(void) { if (likely(!taskletpending)) { taskletpending = 1; taskletsched++; @@ -243,9 +285,78 @@ txerrors++; } } -#else -#define dahdi_dynamic_run __dahdi_dynamic_run -#endif + +static void __dahdi_dynamic_rx(struct dahdi_dynamic *d) { + dahdi_dynamic_process_rxfifo (d); + dahdi_ec_span(&d->span); + dahdi_receive(&d->span); +} + +static void dahdi_dynamic_rx(struct dahdi_dynamic *d) { + if (rxtasklet) { + /* + TODO: Schedule tasklet for this dynamic SPAN. It will be sheduled on the processor/core it was started from. + So, if NIC driver is good - every new IRQ/packet will run on the next processor/core. + */ + } else { + __dahdi_dynamic_rx(d); + } +} + +static void __dahdi_dynamic_tx(struct dahdi_dynamic *d) { + if (noflood && (d->span.alarms & DAHDI_ALARM_RED)) { + /* Flood protection: just send a probe twice once a second during red alarm */ + if (0 != (d->txcnt & 0x1FF)) { + ++d->txcnt; /* If sendmessage not called (not incremented txcnt) - do it here. */ + return; + } + } + dahdi_transmit(&d->span); /* Should we call dahdi_transmit if not going to send ? */ + dahdi_dynamic_sendmessage(d); +} + +static void dahdi_dynamic_flush(void) { + struct dahdi_dynamic_driver *drv; + + rcu_read_lock(); + list_for_each_entry_rcu(drv, &driver_list, list) { + /* Flush any traffic still pending in the driver */ + if (drv->flush) { + drv->flush(); + } + } + rcu_read_unlock(); +} + +static void __dahdi_dynamic_run(void) +{ + struct dahdi_dynamic *d; + + rcu_read_lock(); + list_for_each_entry_rcu(d, &dspan_list, list) { + if(rxtasklet) { + __dahdi_dynamic_rx(d); + } + __dahdi_dynamic_tx(d); /* Handle all transmission. */ + } + rcu_read_unlock(); + dahdi_dynamic_flush(); +} + +static void dahdi_dynamic_run(void) +{ + dahdi_dynamic_start_tasklet(); +} + +static void dahdi_dynamic_tasklet(unsigned long data) +{ + taskletrun++; + if (taskletpending) { + taskletexec++; + __dahdi_dynamic_run(); + } + taskletpending = 0; +} static inline struct dahdi_dynamic *dynamic_from_span(struct dahdi_span *span) { @@ -348,10 +459,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 +483,24 @@ 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); + } + dahdi_dynamic_rx(dtd); /* 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 +522,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 +669,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 +704,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 +750,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 +828,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 +846,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; @@ -713,32 +865,6 @@ return ret; } -#ifdef ENABLE_TASKLETS -static void dahdi_dynamic_tasklet(unsigned long data) -{ - taskletrun++; - if (taskletpending) { - taskletexec++; - __dahdi_dynamic_run(); - } - taskletpending = 0; -} -#else -static void dahdi_dynamic_flush_tasklet(unsigned long data) -{ - struct dahdi_dynamic_driver *drv; - - rcu_read_lock(); - list_for_each_entry_rcu(drv, &driver_list, list) { - /* Flush any traffic still pending in the driver */ - if (drv->flush) { - drv->flush(); - } - } - rcu_read_unlock(); -} -#endif - static int dahdi_dynamic_ioctl(unsigned int cmd, unsigned long data) { struct dahdi_dynamic_span dds; @@ -782,7 +908,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 +965,7 @@ newalarm |= DAHDI_ALARM_RED; if (d->span.alarms != newalarm) { d->span.alarms = newalarm; + checkmaster(); dahdi_alarm_notify(&d->span); alarmchanged++; } @@ -849,10 +976,109 @@ 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"); + + 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"); + 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 +1086,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; @@ -867,14 +1095,11 @@ alarmcheck.function = check_for_red_alarm; /* Check once per second */ mod_timer(&alarmcheck, jiffies + 1 * HZ); -#ifdef ENABLE_TASKLETS tasklet_init(&dahdi_dynamic_tlet, dahdi_dynamic_tasklet, 0); -#else - tasklet_init(&dahdi_dynamic_flush_tlet, dahdi_dynamic_flush_tasklet, 0); -#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, rxtasklet=%d, noflood=%d\n", + rxfifo, rxtasklet, noflood); return 0; } @@ -882,23 +1107,25 @@ { dahdi_set_dynamic_ops(NULL); -#ifdef ENABLE_TASKLETS if (taskletpending) { tasklet_disable(&dahdi_dynamic_tlet); tasklet_kill(&dahdi_dynamic_tlet); } -#else - tasklet_disable(&dahdi_dynamic_flush_tlet); - tasklet_kill(&dahdi_dynamic_flush_tlet); -#endif del_timer_sync(&alarmcheck); /* 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_param(rxtasklet, int, 0600); +MODULE_PARM_DESC(rxtasklet, "Process receive in tasklet, if enabled."); +module_param(noflood, int, 0600); +MODULE_PARM_DESC(noflood, "Flood protection: just send a probe twice once a second during red alarm."); MODULE_DESCRIPTION("DAHDI Dynamic Span Support"); MODULE_AUTHOR("Mark Spencer ");