diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2008-04-15 15:03:40 +0100 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2008-04-15 15:03:40 +0100 |
commit | c6b465270554f67e8fd3074f7ddb9fc531958613 (patch) | |
tree | 2a1d183eff191aa76fa15003faf150b85722b3a1 /xen/common/softirq.c | |
parent | a20735e84bc1d319f523cb179d0bfba764891038 (diff) | |
download | xen-c6b465270554f67e8fd3074f7ddb9fc531958613.tar.gz xen-c6b465270554f67e8fd3074f7ddb9fc531958613.tar.bz2 xen-c6b465270554f67e8fd3074f7ddb9fc531958613.zip |
Tasklet bug fixes.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
Diffstat (limited to 'xen/common/softirq.c')
-rw-r--r-- | xen/common/softirq.c | 61 |
1 files changed, 39 insertions, 22 deletions
diff --git a/xen/common/softirq.c b/xen/common/softirq.c index 1888d17112..be4728f2e0 100644 --- a/xen/common/softirq.c +++ b/xen/common/softirq.c @@ -61,15 +61,18 @@ void tasklet_schedule(struct tasklet *t) spin_lock_irqsave(&tasklet_lock, flags); - if ( !t->is_scheduled ) + if ( !t->is_dead ) { - list_add(&t->list, &tasklet_list); + if ( !t->is_scheduled && !t->is_running ) + { + BUG_ON(!list_empty(&t->list)); + list_add_tail(&t->list, &tasklet_list); + } t->is_scheduled = 1; + raise_softirq(TASKLET_SOFTIRQ); } spin_unlock_irqrestore(&tasklet_lock, flags); - - raise_softirq(TASKLET_SOFTIRQ); } static void tasklet_action(void) @@ -78,24 +81,38 @@ static void tasklet_action(void) spin_lock_irq(&tasklet_lock); - while ( !list_empty(&tasklet_list) ) + if ( list_empty(&tasklet_list) ) { - t = list_entry(tasklet_list.next, struct tasklet, list); - list_del(&t->list); + spin_unlock_irq(&tasklet_lock); + return; + } + + t = list_entry(tasklet_list.next, struct tasklet, list); + list_del_init(&t->list); - BUG_ON(!t->is_scheduled); - t->is_scheduled = 0; + BUG_ON(t->is_dead || t->is_running || !t->is_scheduled); + t->is_scheduled = 0; + t->is_running = 1; - BUG_ON(t->is_running); - t->is_running = 1; + spin_unlock_irq(&tasklet_lock); + t->func(t->data); + spin_lock_irq(&tasklet_lock); - spin_unlock_irq(&tasklet_lock); - t->func(t->data); - spin_lock_irq(&tasklet_lock); + t->is_running = 0; - t->is_running = 0; + if ( t->is_scheduled ) + { + BUG_ON(t->is_dead || !list_empty(&t->list)); + list_add_tail(&t->list, &tasklet_list); } + /* + * If there is more work to do then reschedule. We don't grab more work + * immediately as we want to allow other softirq work to happen first. + */ + if ( !list_empty(&tasklet_list) ) + raise_softirq(TASKLET_SOFTIRQ); + spin_unlock_irq(&tasklet_lock); } @@ -105,12 +122,14 @@ void tasklet_kill(struct tasklet *t) spin_lock_irqsave(&tasklet_lock, flags); - /* De-schedule the tasklet and prevent it from re-scheduling itself. */ if ( !list_empty(&t->list) ) - list_del(&t->list); - t->is_scheduled = 1; + { + BUG_ON(t->is_dead || t->is_running || !t->is_scheduled); + list_del_init(&t->list); + } + t->is_scheduled = 0; + t->is_dead = 1; - /* Wait for tasklet to complete. */ while ( t->is_running ) { spin_unlock_irqrestore(&tasklet_lock, flags); @@ -118,9 +137,6 @@ void tasklet_kill(struct tasklet *t) spin_lock_irqsave(&tasklet_lock, flags); } - /* Clean up and we're done. */ - t->is_scheduled = 0; - spin_unlock_irqrestore(&tasklet_lock, flags); } @@ -128,6 +144,7 @@ void tasklet_init( struct tasklet *t, void (*func)(unsigned long), unsigned long data) { memset(t, 0, sizeof(*t)); + INIT_LIST_HEAD(&t->list); t->func = func; t->data = data; } |