summaryrefslogtreecommitdiff
path: root/libdde_linux26/lib/src/arch/l4/softirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdde_linux26/lib/src/arch/l4/softirq.c')
-rw-r--r--libdde_linux26/lib/src/arch/l4/softirq.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/libdde_linux26/lib/src/arch/l4/softirq.c b/libdde_linux26/lib/src/arch/l4/softirq.c
new file mode 100644
index 00000000..67e8f5aa
--- /dev/null
+++ b/libdde_linux26/lib/src/arch/l4/softirq.c
@@ -0,0 +1,268 @@
+#include "local.h"
+
+#include <linux/interrupt.h>
+
+/* There are at most 32 softirqs in Linux, but only 6 are really used. */
+#define NUM_SOFTIRQS 6
+
+DECLARE_INITVAR(dde26_softirq);
+
+/* softirq threads and their wakeup semaphores */
+ddekit_thread_t *dde_softirq_thread;
+ddekit_sem_t *dde_softirq_sem;
+
+/* struct tasklet_head is not defined in a header in Linux 2.6 */
+struct tasklet_head
+{
+ struct tasklet_struct *list;
+ ddekit_lock_t lock; /* list lock */
+};
+
+/* What to do if a softirq occurs. */
+static struct softirq_action softirq_vec[32];
+
+/* tasklet queues for each softirq thread */
+struct tasklet_head tasklet_vec;
+struct tasklet_head tasklet_hi_vec;
+
+void open_softirq(int nr, void (*action)(struct softirq_action*))
+{
+ softirq_vec[nr].action = action;
+}
+
+static void raise_softirq_irqoff_cpu(unsigned int nr, unsigned int cpu)
+{
+ CHECK_INITVAR(dde26_softirq);
+
+ /* mark softirq scheduled */
+ __raise_softirq_irqoff(nr);
+ /* wake softirq thread */
+ ddekit_sem_up(dde_softirq_sem);
+}
+
+void raise_softirq_irqoff(unsigned int nr)
+{
+ raise_softirq_irqoff_cpu(nr, 0);
+}
+
+void raise_softirq(unsigned int nr)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ raise_softirq_irqoff(nr);
+ local_irq_restore(flags);
+}
+
+/**
+ * Initialize tasklet.
+ */
+void tasklet_init(struct tasklet_struct *t,
+ void (*func)(unsigned long), unsigned long data)
+{
+ t->next = NULL;
+ t->state = 0;
+ atomic_set(&t->count, 0);
+ t->func = func;
+ t->data = data;
+}
+
+/* enqueue tasklet */
+static void __tasklet_enqueue(struct tasklet_struct *t,
+ struct tasklet_head *listhead)
+{
+ ddekit_lock_lock(&listhead->lock);
+ t->next = listhead->list;
+ listhead->list = t;
+ ddekit_lock_unlock(&listhead->lock);
+}
+
+void __tasklet_schedule(struct tasklet_struct *t)
+{
+ unsigned long flags;
+
+ CHECK_INITVAR(dde26_softirq);
+
+ local_irq_save(flags);
+
+ __tasklet_enqueue(t, &tasklet_vec);
+ /* raise softirq */
+ raise_softirq_irqoff_cpu(TASKLET_SOFTIRQ, 0);
+
+ local_irq_restore(flags);
+}
+
+void __tasklet_hi_schedule(struct tasklet_struct *t)
+{
+ unsigned long flags;
+
+ CHECK_INITVAR(dde26_softirq);
+
+ local_irq_save(flags);
+ __tasklet_enqueue(t, &tasklet_hi_vec);
+ raise_softirq_irqoff_cpu(HI_SOFTIRQ, 0);
+ local_irq_restore(flags);
+}
+
+/* Execute tasklets */
+static void tasklet_action(struct softirq_action *a)
+{
+ struct tasklet_struct *list;
+
+ ddekit_lock_lock(&tasklet_vec.lock);
+ list = tasklet_vec.list;
+ tasklet_vec.list = NULL;
+ ddekit_lock_unlock(&tasklet_vec.lock);
+
+ while (list) {
+ struct tasklet_struct *t = list;
+
+ list = list->next;
+
+ if (tasklet_trylock(t)) {
+ if (!atomic_read(&t->count)) {
+ if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
+ BUG();
+ t->func(t->data);
+ tasklet_unlock(t);
+ continue;
+ }
+ tasklet_unlock(t);
+ }
+
+ ddekit_lock_lock(&tasklet_vec.lock);
+ t->next = tasklet_vec.list;
+ tasklet_vec.list = t;
+ raise_softirq_irqoff_cpu(TASKLET_SOFTIRQ, 0);
+ ddekit_lock_unlock(&tasklet_vec.lock);
+ }
+}
+
+
+static void tasklet_hi_action(struct softirq_action *a)
+{
+ struct tasklet_struct *list;
+
+ ddekit_lock_lock(&tasklet_hi_vec.lock);
+ list = tasklet_hi_vec.list;
+ tasklet_hi_vec.list = NULL;
+ ddekit_lock_unlock(&tasklet_hi_vec.lock);
+
+ while (list) {
+ struct tasklet_struct *t = list;
+
+ list = list->next;
+
+ if (tasklet_trylock(t)) {
+ if (!atomic_read(&t->count)) {
+ if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
+ BUG();
+ t->func(t->data);
+ tasklet_unlock(t);
+ continue;
+ }
+ tasklet_unlock(t);
+ }
+
+ ddekit_lock_lock(&tasklet_hi_vec.lock);
+ t->next = tasklet_hi_vec.list;
+ tasklet_hi_vec.list = t;
+ raise_softirq_irqoff_cpu(HI_SOFTIRQ, 0);
+ ddekit_lock_unlock(&tasklet_hi_vec.lock);
+ }
+}
+
+#define MAX_SOFTIRQ_RETRIES 10
+
+/** Run softirq handlers
+ */
+void __do_softirq(void)
+{
+ int retries = MAX_SOFTIRQ_RETRIES;
+ do {
+ struct softirq_action *h = softirq_vec;
+ unsigned long pending = local_softirq_pending();
+
+ /* reset softirq count */
+ set_softirq_pending(0);
+ local_irq_enable();
+
+ /* While we have a softirq pending... */
+ while (pending) {
+ /* need to execute current softirq? */
+ if (pending & 1)
+ h->action(h);
+ /* try next softirq */
+ h++;
+ /* remove pending flag for last softirq */
+ pending >>= 1;
+ }
+ local_irq_disable();
+
+ /* Somebody might have scheduled another softirq in between
+ * (e.g., an IRQ thread or another tasklet). */
+ } while (local_softirq_pending() && --retries);
+
+}
+
+
+void do_softirq(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (local_softirq_pending())
+ __do_softirq();
+ local_irq_restore(flags);
+}
+
+/** Softirq thread function.
+ *
+ * Once started, a softirq thread waits for tasklets to be scheduled
+ * and executes them.
+ *
+ * \param arg # of this softirq thread so that it grabs the correct lock
+ * if multiple softirq threads are running.
+ */
+void l4dde26_softirq_thread(void *arg)
+{
+ printk("Softirq daemon starting\n");
+ l4dde26_process_add_worker();
+
+ /* This thread will always be in a softirq, so set the
+ * corresponding flag right now.
+ */
+ preempt_count() |= SOFTIRQ_MASK;
+
+ while(1) {
+ ddekit_sem_down(dde_softirq_sem);
+ do_softirq();
+ }
+}
+
+/** Initialize softirq subsystem.
+ *
+ * Start NUM_SOFTIRQ_THREADS threads executing the \ref l4dde26_softirq_thread
+ * function.
+ */
+void l4dde26_softirq_init(void)
+{
+ char name[20];
+
+ dde_softirq_sem = ddekit_sem_init(0);
+
+ set_softirq_pending(0);
+
+ ddekit_lock_init_unlocked(&tasklet_vec.lock);
+ ddekit_lock_init_unlocked(&tasklet_hi_vec.lock);
+
+ snprintf(name, 20, ".softirqd");
+ dde_softirq_thread = ddekit_thread_create(
+ l4dde26_softirq_thread,
+ NULL, name);
+
+ open_softirq(TASKLET_SOFTIRQ, tasklet_action);
+ open_softirq(HI_SOFTIRQ, tasklet_hi_action);
+
+ INITIALIZE_INITVAR(dde26_softirq);
+}