[PATCH 24/32] leon, grtm: SMP support by using spin-locks
Daniel Hellstrom
daniel at gaisler.com
Thu May 11 14:26:09 UTC 2017
---
c/src/lib/libbsp/sparc/shared/tmtc/grtm.c | 198 +++++++++++++++++-------------
1 file changed, 115 insertions(+), 83 deletions(-)
diff --git a/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c b/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c
index a1038d4..bd7d98f 100644
--- a/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c
+++ b/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c
@@ -23,17 +23,15 @@
#include <drvmgr/ambapp_bus.h>
#include <bsp/grtm.h>
-#ifndef IRQ_GLOBAL_PREPARE
- #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level
-#endif
-
-#ifndef IRQ_GLOBAL_DISABLE
- #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level)
-#endif
-
-#ifndef IRQ_GLOBAL_ENABLE
- #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level)
-#endif
+/* map via rtems_interrupt_lock_* API: */
+#define SPIN_DECLARE(lock) RTEMS_INTERRUPT_LOCK_MEMBER(lock)
+#define SPIN_INIT(lock, name) rtems_interrupt_lock_initialize(lock, name)
+#define SPIN_LOCK(lock, level) rtems_interrupt_lock_acquire_isr(lock, &level)
+#define SPIN_LOCK_IRQ(lock, level) rtems_interrupt_lock_acquire(lock, &level)
+#define SPIN_UNLOCK(lock, level) rtems_interrupt_lock_release_isr(lock, &level)
+#define SPIN_UNLOCK_IRQ(lock, level) rtems_interrupt_lock_release(lock, &level)
+#define SPIN_IRQFLAGS(k) rtems_interrupt_lock_context k
+#define SPIN_ISR_IRQFLAGS(k) SPIN_IRQFLAGS(k)
/*
#define DEBUG
@@ -364,6 +362,7 @@ struct grtm_priv {
int irq;
int minor;
int subrev; /* GRTM Revision */
+ SPIN_DECLARE(devlock); /* spin-lock ISR protection */
int open;
int running;
@@ -511,6 +510,8 @@ static int grtm_init3(struct drvmgr_dev *dev)
sprintf(priv->devName, "/dev/%sgrtm%d", prefix, dev->minor_bus);
}
+ SPIN_INIT(&priv->devlock, priv->devName);
+
/* Register Device */
status = rtems_io_register_name(priv->devName, grtm_driver_io_major, dev->minor_drv);
if (status != RTEMS_SUCCESSFUL) {
@@ -938,10 +939,11 @@ static rtems_device_driver grtm_close(rtems_device_major_number major, rtems_dev
pDev = (struct grtm_priv *)dev->priv;
if ( pDev->running ){
+ drvmgr_interrupt_unregister(dev, 0, grtm_interrupt, pDev);
grtm_stop(pDev);
pDev->running = 0;
}
-
+
/* Reset core */
grtm_hw_reset(pDev);
@@ -1067,13 +1069,12 @@ static int grtm_free_sent(struct grtm_priv *pDev)
* Return Value
* Returns number of frames moved from ready to scheduled queue
*/
-static int grtm_schedule_ready(struct grtm_priv *pDev, int ints_off)
+static int grtm_schedule_ready(struct grtm_priv *pDev)
{
int cnt;
unsigned int ctrl, dmactrl;
struct grtm_ring *curr_bd;
struct grtm_frame *curr_frm, *last_frm;
- IRQ_GLOBAL_PREPARE(oldLevel);
if ( !pDev->ready.head ){
return 0;
@@ -1165,23 +1166,83 @@ static int grtm_schedule_ready(struct grtm_priv *pDev, int ints_off)
/* Update TX ring posistion */
pDev->ring = curr_bd;
- if ( !ints_off ) {
- IRQ_GLOBAL_DISABLE(oldLevel);
- }
/* Make hardware aware of the newly enabled descriptors */
dmactrl = READ_REG(&pDev->regs->dma_ctrl);
dmactrl &= ~(GRTM_DMA_CTRL_TXRST | GRTM_DMA_CTRL_RST);
dmactrl |= GRTM_DMA_CTRL_EN;
pDev->regs->dma_ctrl = dmactrl;
-
- if ( !ints_off ) {
- IRQ_GLOBAL_ENABLE(oldLevel);
- }
}
+
return cnt;
}
+static void grtm_tx_process(struct grtm_priv *pDev)
+{
+ int num;
+
+ /* Free used descriptors and put the sent frame into the "Sent queue"
+ * (SCHEDULED->SENT)
+ */
+ num = grtm_free_sent(pDev);
+ pDev->scheduled_cnt -= num;
+ pDev->sent_cnt += num;
+
+ /* Use all available free descriptors there are frames for
+ * in the ready queue.
+ * (READY->SCHEDULED)
+ */
+ if (pDev->running) {
+ num = grtm_schedule_ready(pDev);
+ pDev->ready_cnt -= num;
+ pDev->scheduled_cnt += num;
+ }
+}
+
+/*
+ * The TX lock protects user tasks from the ISR. If TX DMA interrupt occurs
+ * while the user task is processing the TX DMA descriptors the ISR will
+ * ignore interrupt the request by not processing the DMA table since that
+ * is done by the user task anyway. In SMP, when a user task enters the TX DMA
+ * processing while the ISR (on another CPU) is also processing the user task
+ * will loop waiting for the ISR to complete.
+ */
+static int grtm_request_txlock(struct grtm_priv *pDev, int block)
+{
+ SPIN_IRQFLAGS(irqflags);
+ int got_lock = 0;
+
+ do {
+ SPIN_LOCK_IRQ(&pDev->devlock, irqflags);
+ if (pDev->handling_transmission == 0) {
+ pDev->handling_transmission = 1;
+ got_lock = 1;
+ }
+ SPIN_UNLOCK_IRQ(&pDev->devlock, irqflags);
+ } while (!got_lock && block);
+
+ return got_lock;
+}
+
+static inline int grtm_request_txlock_isr(struct grtm_priv *pDev)
+{
+ SPIN_ISR_IRQFLAGS(irqflags);
+ int got_lock = 0;
+
+ SPIN_LOCK(&pDev->devlock, irqflags);
+ if (pDev->handling_transmission == 0) {
+ pDev->handling_transmission = 1;
+ got_lock = 1;
+ }
+ SPIN_UNLOCK(&pDev->devlock, irqflags);
+
+ return got_lock;
+}
+
+static inline void grtm_release_txlock(struct grtm_priv *pDev)
+{
+ pDev->handling_transmission = 0;
+}
static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
@@ -1192,7 +1253,6 @@ static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_dev
int status;
struct grtm_ioc_config *cfg;
struct grtm_ioc_hw_status *hwregs;
- IRQ_GLOBAL_PREPARE(oldLevel);
struct grtm_list *chain;
struct grtm_frame *curr;
struct grtm_ioc_hw *hwimpl;
@@ -1314,9 +1374,7 @@ static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_dev
return RTEMS_INVALID_NAME;
}
/* We disable interrupt in order to get a snapshot of the registers */
- IRQ_GLOBAL_DISABLE(oldLevel);
/* TODO: implement hwregs */
- IRQ_GLOBAL_ENABLE(oldLevel);
break;
/* Put a chain of frames at the back of the "Ready frames" queue. This
@@ -1328,16 +1386,20 @@ static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_dev
if ( !pDev->running ){
return RTEMS_RESOURCE_IN_USE;
}
- num=0;
/* Get pointer to frame chain wished be sent */
chain = (struct grtm_list *)ioarg->buffer;
if ( !chain ){
/* No new frames to send ==> just trigger hardware
* to send previously made ready frames to be sent.
+ * If someone else is processing the DMA we igore the
+ * request.
*/
- pDev->handling_transmission = 1;
- goto trigger_transmission;
+ if (grtm_request_txlock(pDev, 0)) {
+ grtm_tx_process(pDev);
+ grtm_release_txlock(pDev);
+ }
+ break;
}
if ( !chain->tail || !chain->head ){
return RTEMS_INVALID_NAME;
@@ -1347,6 +1409,7 @@ static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_dev
/* Mark ready frames unsent by clearing GRTM_FLAGS_SENT of all frames */
+ num = 0;
curr = chain->head;
while(curr != chain->tail){
curr->flags = curr->flags & ~(GRTM_FLAGS_SENT|GRRM_FLAGS_ERR);
@@ -1356,7 +1419,9 @@ static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_dev
curr->flags = curr->flags & ~(GRTM_FLAGS_SENT|GRRM_FLAGS_ERR);
num++;
- pDev->handling_transmission = 1;
+ /* wait until we get the device lock */
+ grtm_request_txlock(pDev, 1);
+
/* 1. Put frames into ready queue
* (New Frames->READY)
*/
@@ -1374,23 +1439,12 @@ static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_dev
chain->tail->next = NULL;
}
pDev->ready_cnt += num; /* Added 'num' frames to ready queue */
-trigger_transmission:
- /* 2. Free used descriptors and put the sent frame into the "Sent queue"
- * (SCHEDULED->SENT)
- */
- num = grtm_free_sent(pDev);
- pDev->scheduled_cnt -= num;
- pDev->sent_cnt += num;
- /* 3. Use all available free descriptors there are frames for
- * in the ready queue.
- * (READY->SCHEDULED)
+ /* 2. SCHEDULED->SENT
+ * 3. READY->SCHEDULED
*/
- num = grtm_schedule_ready(pDev,0);
- pDev->ready_cnt -= num;
- pDev->scheduled_cnt += num;
-
- pDev->handling_transmission = 0;
+ grtm_tx_process(pDev);
+ grtm_release_txlock(pDev);
break;
/* Take all available sent frames from the "Sent frames" queue.
@@ -1413,25 +1467,15 @@ trigger_transmission:
}
/* Lock out interrupt handler */
- pDev->handling_transmission = 1;
+ grtm_request_txlock(pDev, 1);
do {
- /* Move sent frames from descriptors to Sent queue. This makes more
- * descriptors (BDs) available.
+ /* Process descriptor table and populate with new
+ * buffers:
+ * * SCHEDULED->SENT
+ * * READY->SCHEDULED
*/
- num = grtm_free_sent(pDev);
- pDev->scheduled_cnt -= num;
- pDev->sent_cnt += num;
-
-
- if ( pDev->running ){
- /* Fill descriptors with as many frames from the ready list
- * as possible.
- */
- num = grtm_schedule_ready(pDev,0);
- pDev->ready_cnt -= num;
- pDev->scheduled_cnt += num;
- }
+ grtm_tx_process(pDev);
/* Are there any frames on the sent queue waiting to be
* reclaimed?
@@ -1445,14 +1489,14 @@ trigger_transmission:
if ( pDev->running && pDev->config.blocking ){
ret = rtems_semaphore_obtain(pDev->sem_tx,RTEMS_WAIT,pDev->config.timeout);
if ( ret == RTEMS_TIMEOUT ) {
- pDev->handling_transmission = 0;
+ grtm_release_txlock(pDev);
return RTEMS_TIMEOUT;
} else if ( ret == RTEMS_SUCCESSFUL ) {
/* There might be frames available, go check */
continue;
} else {
/* any error (driver closed, internal error etc.) */
- pDev->handling_transmission = 0;
+ grtm_release_txlock(pDev);
return RTEMS_UNSATISFIED;
}
@@ -1461,7 +1505,7 @@ trigger_transmission:
chain->head = NULL;
chain->tail = NULL;
/* do not lock out interrupt handler any more */
- pDev->handling_transmission = 0;
+ grtm_release_txlock(pDev);
return RTEMS_TIMEOUT;
}
}else{
@@ -1481,7 +1525,7 @@ trigger_transmission:
}while(1);
/* do not lock out interrupt handler any more */
- pDev->handling_transmission = 0;
+ grtm_release_txlock(pDev);
break;
default:
@@ -1495,17 +1539,15 @@ static void grtm_interrupt(void *arg)
struct grtm_priv *pDev = arg;
struct grtm_regs *regs = pDev->regs;
unsigned int status;
- int num;
/* Clear interrupt by reading it */
status = READ_REG(®s->dma_status);
-
+
/* Spurious Interrupt? */
- if ( !pDev->running )
+ if ( !pDev->running || !status)
return;
- if ( status )
- regs->dma_status = status;
+ regs->dma_status = status;
if ( status & GRTM_DMA_STS_TFF ){
pDev->stats.err_transfer_frame++;
@@ -1521,21 +1563,11 @@ static void grtm_interrupt(void *arg)
if ( status & GRTM_DMA_STS_TI ){
- if ( pDev->config.isr_desc_proc && !pDev->handling_transmission ) {
- /* Free used descriptors and put the sent frame into the "Sent queue"
- * (SCHEDULED->SENT)
- */
- num = grtm_free_sent(pDev);
- pDev->scheduled_cnt -= num;
- pDev->sent_cnt += num;
-
- /* Use all available free descriptors there are frames for
- * in the ready queue.
- * (READY->SCHEDULED)
- */
- num = grtm_schedule_ready(pDev,1);
- pDev->ready_cnt -= num;
- pDev->scheduled_cnt += num;
+ if ( pDev->config.isr_desc_proc) {
+ if (grtm_request_txlock_isr(pDev)) {
+ grtm_tx_process(pDev);
+ grtm_release_txlock(pDev);
+ }
#if 0
if ( (pDev->config.blocking==GRTM_BLKMODE_COMPLETE) && pDev->timeout ){
--
2.7.4
More information about the devel
mailing list