diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c index eff2957..186d182 100644 --- a/drivers/dahdi/dahdi-base.c +++ b/drivers/dahdi/dahdi-base.c @@ -34,6 +34,16 @@ * this program for more details. */ +/* + * HiRes timer tick function ported from dahdi_dummy by Michael Walton + * + */ + +#include + +#if defined(CONFIG_HIGH_RES_TIMERS) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define USE_HIGHRESTIMER +#endif #include #include @@ -58,6 +68,18 @@ #include #include + +#if defined(USE_HIGHRESTIMER) +#include +#endif + +#ifdef USE_HIGHRESTIMER +#define CLOCK_SRC "HRtimer" +static struct hrtimer dahdi_hrtimer; +#define DAHDI_RATE 1000 /* DAHDI ticks per second */ +#define DAHDI_TIME (1000000 / DAHDI_RATE) /* DAHDI tick time in us */ +#define DAHDI_TIME_NS (DAHDI_TIME * 1000) /* DAHDI tick time in ns */ +#endif #define DAHDI_PRINK_MACROS_USE_debug @@ -9947,6 +9969,98 @@ #else +#if defined(USE_HIGHRESTIMER) + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) +/* Compatibility with new hrtimer interface */ +static inline ktime_t hrtimer_get_expires(const struct hrtimer *timer) +{ + return timer->expires; +} + +static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) +{ + timer->expires = time; +} +#endif + +static enum hrtimer_restart dahdi_hr_int(struct hrtimer *htmr) +{ + unsigned long overrun; + unsigned long flags; + + if (atomic_read(&core_timer.count) == atomic_read(&core_timer.last_count)) { + /* This is the code path if a board driver is not calling + * dahdi_receive, and therefore the core of dahdi needs to + * perform the master span processing itself. */ + if (core_timer.dahdi_receive_used) { + core_timer.dahdi_receive_used = 0; + printk(KERN_NOTICE "dahdi_hr_int: enabled master core hrtimer\n"); + } + /* Overrun should always return 1, since we are in the timer that + * expired. + * We should worry if overrun is 2 or more; then we really missed + * a tick */ + overrun = hrtimer_forward(htmr, hrtimer_get_expires(htmr), + ktime_set(0, DAHDI_TIME_NS)); + if (overrun > 1) { + if (printk_ratelimit()) { + printk(KERN_NOTICE "dahdi_hr_int: hrtimer missed %lu ticks\n", + overrun - 1); + } + } + local_irq_save(flags); + _process_masterspan(); + local_irq_restore(flags); + } else { + /* board driver is spinning DAHDI, relax, check back in 1 second */ + if (debug) + printk(KERN_DEBUG "dahdi_hr_int: slow hrtimer tick\n"); + + hrtimer_forward(htmr, hrtimer_get_expires(htmr), ktime_set(1, 0)); + + if (!core_timer.dahdi_receive_used) { + core_timer.dahdi_receive_used = 1; + printk(KERN_NOTICE "dahdi_hr_int: disabled master core hrtimer\n"); + } + } + atomic_set(&core_timer.last_count, atomic_read(&core_timer.count)); + + if (debug) { + static int count = 0; + /* Printk every 5 seconds, good test to see if timer is + * running properly */ + if (count++ % 5000 == 0) + printk(KERN_DEBUG "dahdi_hr_int: 5000 ticks from hrtimer\n"); + } + + /* Always restart the timer */ + return HRTIMER_RESTART; +} + +static int dahdi_hr_init(void) +{ + printk(KERN_DEBUG "dahdi_hr_init: Trying to load High Resolution Timer\n"); + hrtimer_init(&dahdi_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + printk(KERN_DEBUG "dahdi_hr_init: Initialized High Resolution Timer\n"); + + /* Set timer callback function */ + dahdi_hrtimer.function = dahdi_hr_int; + + printk(KERN_DEBUG "dahdi_hr_init: Starting High Resolution Timer\n"); + hrtimer_start(&dahdi_hrtimer, ktime_set(0, DAHDI_TIME_NS), HRTIMER_MODE_REL); + printk(KERN_INFO "dahdi_hr_init: High Resolution Timer started, good to go\n"); + + return 0; +} + +static void dahdi_hr_destroy(void) +{ + hrtimer_cancel(&dahdi_hrtimer); +} + +#else + static unsigned long core_diff_ms(struct timespec *t0, struct timespec *t1) { long nanosec, sec; @@ -10049,8 +10163,15 @@ } } +#endif /* !USE_HIGHRESTIMER */ + static void coretimer_init(void) { +#ifdef USE_HIGHRESTIMER + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.shutdown, 0); + dahdi_hr_init(); +#else init_timer(&core_timer.timer); core_timer.timer.function = coretimer_func; ktime_get_ts(&core_timer.start_interval); @@ -10061,12 +10182,17 @@ core_timer.interval = (HZ/250); core_timer.timer.expires = jiffies + core_timer.interval; add_timer(&core_timer.timer); +#endif } static void coretimer_cleanup(void) { atomic_set(&core_timer.shutdown, 1); +#ifdef USE_HIGHRESTIMER + dahdi_hr_destroy(); +#else del_timer_sync(&core_timer.timer); +#endif } #endif /* CONFIG_DAHDI_CORE_TIMER */ @@ -10173,6 +10299,7 @@ return 0; } EXPORT_SYMBOL(_dahdi_receive); +EXPORT_SYMBOL(dahdi_is_sync_master); MODULE_AUTHOR("Mark Spencer "); MODULE_DESCRIPTION("DAHDI Telephony Interface"); diff --git a/drivers/dahdi/dahdi_dynamic.c b/drivers/dahdi/dahdi_dynamic.c index 72fbf5e..6f3c6aa 100644 --- a/drivers/dahdi/dahdi_dynamic.c +++ b/drivers/dahdi/dahdi_dynamic.c @@ -22,6 +22,22 @@ * this program for more details. */ +/* + * Dynamic multispan functionality added by + * Michael Walton based on original work by + * Pavel Selivanov + * + * Dynamic multispan introduces: + * + * 1. Configurable fifo on incoming dynamic frames + * 2. Master/slave dynamic spans with priority switching + * 3. All transmit and receive processing is done in the master span receive + * event to ensure proper handling of jitter and phase differences + * 4. Unreachable spans move to a poll mode to prevent network being flooded + * by 1ms frames that go nowhere + * + */ + #include #include #include @@ -32,6 +48,8 @@ #include #include #include +#include +#include #include @@ -47,7 +65,7 @@ * tasklets. */ -#undef ENABLE_TASKLETS +#define ENABLE_TASKLETS /* * Dynamic spans implemented using TDM over X with standard message @@ -100,20 +118,29 @@ static LIST_HEAD(driver_list); static int debug = 0; +static int rxfifo = 2; +static int tasklet = 1; +static int noflood = 1; static int hasmaster = 0; +static int mastertiming = 0; static void checkmaster(void) { int newhasmaster=0; int best = 9999999; struct dahdi_dynamic *d, *master = NULL; + struct dahdi_dynamic *dspans = NULL; rcu_read_lock(); list_for_each_entry_rcu(d, &dspan_list, list) { + if (dspans == NULL) + dspans = d; if (d->timing) { d->master = 0; + d->span.flags &= ~DAHDI_FLAG_RUNNING; + d->span.syncsrc = 0; if (!(d->span.alarms & DAHDI_ALARM_RED) && (d->timing < best)) { /* If not in alarm and they're @@ -125,17 +152,31 @@ } } - hasmaster = newhasmaster; - /* Mark the new master if there is one */ - if (master) - master->master = 1; - rcu_read_unlock(); - - if (master) - printk(KERN_INFO "TDMoX: New master: %s\n", master->span.name); - else - printk(KERN_INFO "TDMoX: No master.\n"); + if (master && !hasmaster) + printk(KERN_DEBUG "TDMoX: New master: %s\n", master->span.name); + else if (!master && hasmaster) + printk(KERN_DEBUG "TDMoX: No master\n"); + hasmaster = newhasmaster; + if (hasmaster && master) { + mastertiming = master->timing; + } else { + mastertiming = 0; + } + /* Mark the new master if there is one */ + if (master) { + /* Only this span of dynamic spans can be a master for DAHDI */ + master->span.flags |= DAHDI_FLAG_RUNNING; + master->span.syncsrc = master->span.spanno; + master->master = 1; + /* force DAHDI timing to wake up and recalculate! */ + master->span.lastalarms = DAHDI_ALARM_RED; + dahdi_alarm_notify(&(master->span)); + } else if (dspans) { + /* force DAHDI timing to wake up - let it select another master! */ + dspans->span.lastalarms = 0; + dahdi_alarm_notify(&(dspans->span)); + } } static void dahdi_dynamic_sendmessage(struct dahdi_dynamic *d) @@ -198,6 +239,25 @@ } +static void dahdi_process_rxfifo (struct dahdi_dynamic *d) { + unsigned char *msg = d->msgbuf2; + int x = d->span.channels * DAHDI_CHUNKSIZE; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + if (__kfifo_get(d->rxfifo, msg, x) != x) { +#else + if (kfifo_out(&d->rxfifo, msg, x) != x) { +#endif + ++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_run(void) { struct dahdi_dynamic *d; @@ -205,12 +265,25 @@ rcu_read_lock(); list_for_each_entry_rcu(d, &dspan_list, list) { + dahdi_process_rxfifo(d); + dahdi_ec_span(&d->span); + if (!(d->span.alarms & DAHDI_ALARM_RED)) { + dahdi_receive(&d->span); + } dahdi_transmit(&d->span); /* Handle all transmissions now */ - dahdi_dynamic_sendmessage(d); + if (noflood && (d->span.alarms & DAHDI_ALARM_RED)) { + /* Flood protection: just send a probe twice once a second during red alarm */ + d->txcnt++; + if ((d->txcnt % 512) == 0) { + dahdi_dynamic_sendmessage(d); + } + } else { + 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 @@ -235,7 +308,9 @@ #ifdef ENABLE_TASKLETS static void dahdi_dynamic_run(void) { - if (likely(!taskletpending)) { + if (!tasklet) { + __dahdi_dynamic_run(); + } else if (likely(!taskletpending)) { taskletpending = 1; taskletsched++; tasklet_hi_schedule(&dahdi_dynamic_tlet); @@ -263,13 +338,16 @@ int newalarm; unsigned short rxpos, rxcnt; + if (dtd->locked) + return; + rcu_read_lock(); if (unlikely(msglen < 6)) { rcu_read_unlock(); newerr = ERR_LEN; if (newerr != dtd->err) - printk(KERN_NOTICE "Span %s: Insufficient samples for header (only %d)\n", span->name, msglen); + printk(KERN_DEBUG "Span %s: Insufficient samples for header (only %d)\n", span->name, msglen); dtd->err = newerr; return; } @@ -279,7 +357,7 @@ rcu_read_unlock(); newerr = ERR_NSAMP | msg[0]; if (newerr != dtd->err) - printk(KERN_NOTICE "Span %s: Expected %d samples, but receiving %d\n", span->name, DAHDI_CHUNKSIZE, msg[0]); + printk(KERN_DEBUG "Span %s: Expected %d samples, but receiving %d\n", span->name, DAHDI_CHUNKSIZE, msg[0]); dtd->err = newerr; return; } @@ -296,7 +374,7 @@ rcu_read_unlock(); newerr = ERR_NCHAN | nchans; if (newerr != dtd->err) - printk(KERN_NOTICE "Span %s: Expected %d channels, but receiving %d\n", span->name, span->channels, nchans); + printk(KERN_DEBUG "Span %s: Expected %d channels, but receiving %d\n", span->name, span->channels, nchans); dtd->err = newerr; return; } @@ -320,7 +398,7 @@ rcu_read_unlock(); newerr = ERR_LEN | xlen; if (newerr != dtd->err) - printk(KERN_NOTICE "Span %s: Expected message size %d, but was %d instead\n", span->name, xlen, msglen); + printk(KERN_DEBUG "Span %s: Expected message size %d, but was %d instead\n", span->name, xlen, msglen); dtd->err = newerr; return; } @@ -347,11 +425,21 @@ } } - /* Record data for channels */ - for (x=0;xchans[x]->readchunk, msg, DAHDI_CHUNKSIZE); - msg += DAHDI_CHUNKSIZE; - } + /* Record data for channels in fifo */ + x = nchans*DAHDI_CHUNKSIZE; + /* + * kfifo_alloc is aligned by ^2 bytes. Make sure we have enough space + * in fifo for the whole packet. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + if (dtd->rxfifo->size >= __kfifo_len (dtd->rxfifo) + x) + __kfifo_put(dtd->rxfifo, msg, x); +#else + if (kfifo_size(&dtd->rxfifo) >= kfifo_len(&dtd->rxfifo) + x) + kfifo_in(&dtd->rxfifo, msg, x); +#endif + else + ++dtd->skip; master = dtd->master; @@ -370,19 +458,22 @@ if (newalarm != span->alarms) { span->alarms = newalarm; + if (newalarm & DAHDI_ALARM_RED) { + span->flags &= ~DAHDI_FLAG_RUNNING; + } dahdi_alarm_notify(span); checkmaster(); } /* 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); + printk(KERN_DEBUG "Span %s: Expected seq no %d, but received %d instead\n", span->name, rxcnt, rxpos); dahdi_ec_span(span); dahdi_receive(span); /* If this is our master span, then run everything */ - if (master) + if (dahdi_is_sync_master(&dtd->span)) dahdi_dynamic_run(); } EXPORT_SYMBOL(dahdi_dynamic_receive); @@ -401,11 +492,19 @@ WARN_ON(test_bit(DAHDI_FLAGBIT_REGISTERED, &d->span.flags)); kfree(d->msgbuf); + kfree(d->msgbuf2); for (x = 0; x < d->span.channels; x++) kfree(d->chans[x]); dahdi_free_device(d->ddev); + /* Free rxfifo */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + if (d->rxfifo) + kfree(d->rxfifo); +#else + kfifo_free(&d->rxfifo); +#endif kfree(d); } @@ -546,15 +645,25 @@ struct dahdi_dynamic *head; struct dahdi_dynamic *d = dynamic_from_span(span); - if (hasmaster) - return; + if (hasmaster) { + /* + * We have a valid master span - only spin on the master span if not + * the overall DAHDI master + */ + if (d->master && !is_master) { + dahdi_dynamic_run(); + } + } else { + /* + * We don't have a valid master span, spin on just the first span + */ + rcu_read_lock(); + head = list_entry(dspan_list.next, struct dahdi_dynamic, list); + rcu_read_unlock(); - rcu_read_lock(); - head = list_entry(dspan_list.next, struct dahdi_dynamic, list); - rcu_read_unlock(); - - if (d == head) - dahdi_dynamic_run(); + if (d == head) + dahdi_dynamic_run(); + } return; } @@ -613,12 +722,21 @@ bufsize = dds->numchans * DAHDI_CHUNKSIZE + dds->numchans / 4 + 48; d->msgbuf = kzalloc(bufsize, GFP_KERNEL); + d->msgbuf2 = kzalloc(bufsize, GFP_KERNEL); - if (!d->msgbuf) { + if (!d->msgbuf || !d->msgbuf2) { dynamic_put(d); return -ENOMEM; } + /* Allocate rx fifo */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + d->rxfifo = kfifo_alloc ((dds->numchans * DAHDI_CHUNKSIZE) * rxfifo, + GFP_KERNEL, NULL); +#else + kfifo_alloc(&d->rxfifo, (dds->numchans * DAHDI_CHUNKSIZE) * rxfifo, GFP_KERNEL); +#endif + /* 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)); @@ -667,6 +785,10 @@ * the dahdi_dyanmic here. Do not access dtd directly now. */ d->driver = dtd; + spin_lock_irqsave(&driver_lock, flags); + d->locked = 1; + spin_unlock_irqrestore(&driver_lock, flags); + /* Create the stuff */ res = dtd->create(d, d->addr); if (res) { @@ -696,6 +818,7 @@ * this point. It also must remain on the list while registered. */ spin_lock_irqsave(&dspan_lock, flags); list_add_rcu(&d->list, &dspan_list); + d->locked = 0; spin_unlock_irqrestore(&dspan_lock, flags); checkmaster(); @@ -846,7 +969,7 @@ } rcu_read_unlock(); - if (alarmchanged) + if (alarmchanged || !hasmaster) checkmaster(); /* Do the next one */ @@ -858,8 +981,89 @@ .ioctl = dahdi_dynamic_ioctl, }; +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_entry; +static const char *ztd_procname="dahdi/dahdi_dynamic_stats"; +static int dahdi_dynamic_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + struct dahdi_dynamic *z; + + /* In Linux 2.6, this MUST NOT EXCEED 4096 bytes in one read! */ + + len = sprintf(page+len, "dahdi_dynamic info/statistics\n"); + +#ifdef ENABLE_TASKLETS + len += sprintf(page+len, "\ttaskletrun: %d\n", taskletrun); + len += sprintf(page+len, "\ttaskletsched: %d\n", taskletsched); + len += sprintf(page+len, "\ttaskletexec: %d\n", taskletexec); + len += sprintf(page+len, "\ttxerrors: %d\n", txerrors); + len += sprintf(page+len, "\ttaskletpending: %d\n", taskletpending); +#endif + + len += sprintf(page + len, "\n"); + spin_lock_irqsave(&driver_lock, flags); + list_for_each_entry_rcu(z, &dspan_list, list) { + struct dahdi_span *span = &(z->span); + if (span->name) + len += sprintf(page + len, "Span %d: %s ", span->spanno, span->name); + if (span->desc) + len += sprintf(page + len, "\"%s\"", span->desc); + else + len += sprintf(page + len, "\"\""); + + len += sprintf(page + len, " "); + if (z->master) + len += sprintf(page + len, "ClockSource "); + len += sprintf(page + len, "\n"); + + len += sprintf(page+len, "\tslip: %d", z->slip); + len += sprintf(page+len, ", skip: %d", z->skip); + len += sprintf(page+len, ", rxnuerr: %d", z->rxnuerr); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + len += sprintf(page+len, ", rxfifo: %d", __kfifo_len(z->rxfifo)/span->channels/DAHDI_CHUNKSIZE); +#else + len += sprintf(page+len, ", rxfifo: %d", kfifo_len(&z->rxfifo)/span->channels/DAHDI_CHUNKSIZE); +#endif + len += sprintf(page + len, "\n"); + + if (len <= off) { /* If everything printed so far is before beginning of request */ + off -= len; + len = 0; + } + if (len > off+count) /* stop if we've already generated enough */ + break; + } + spin_unlock_irqrestore(&driver_lock, flags); + + if (len <= off) { /* If everything printed so far is before beginning of request */ + off -= len; + len = 0; + } + *start = page + off; + len -= off; /* un-count any remaining offset */ + if (len > count) + len = count; /* don't return bytes not asked for */ + return len; +} +#endif /* CONFIG_PROC_FS */ + +static void proc_init (void) { +#ifdef CONFIG_PROC_FS + proc_entry = create_proc_read_entry(ztd_procname, 0444, NULL , dahdi_dynamic_proc_read, NULL); +#endif /* CONFIG_PROC_FS */ +} +static void proc_cleanup (void) { +#ifdef CONFIG_PROC_FS + remove_proc_entry(ztd_procname, NULL); +#endif /* CONFIG_PROC_FS */ +} + 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; @@ -868,13 +1072,14 @@ /* Check once per second */ mod_timer(&alarmcheck, jiffies + 1 * HZ); #ifdef ENABLE_TASKLETS - tasklet_init(&dahdi_dynamic_tlet, dahdi_dynamic_tasklet, 0); + if (tasklet) + 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 tasklet=%d\n", rxfifo, tasklet); return 0; } @@ -883,7 +1088,7 @@ dahdi_set_dynamic_ops(NULL); #ifdef ENABLE_TASKLETS - if (taskletpending) { + if (tasklet && taskletpending) { tasklet_disable(&dahdi_dynamic_tlet); tasklet_kill(&dahdi_dynamic_tlet); } @@ -895,10 +1100,15 @@ /* 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_param(tasklet, int, 0600); +module_param(noflood, int, 0600); +MODULE_PARM_DESC(rxfifo, "RX fifo size (milliseconds)."); MODULE_DESCRIPTION("DAHDI Dynamic Span Support"); MODULE_AUTHOR("Mark Spencer "); diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h index 8f3dc76..1152e80 100644 --- a/include/dahdi/kernel.h +++ b/include/dahdi/kernel.h @@ -57,6 +57,7 @@ #include #include +#include #define dahdi_pci_module pci_register_driver @@ -1094,6 +1095,17 @@ int master; unsigned char *msgbuf; struct device *dev; + /* Additional multispan context variables */ + unsigned char *msgbuf2; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + struct kfifo *rxfifo; +#else + struct kfifo rxfifo; +#endif + unsigned int slip; + unsigned int skip; + unsigned int rxnuerr; + int locked; struct list_head list; };