linux-bk/kernel/softirq.c
<<
>>
Prefs
   1/*
   2 *      linux/kernel/softirq.c
   3 *
   4 *      Copyright (C) 1992 Linus Torvalds
   5 *
   6 * Fixed a disable_bh()/enable_bh() race (was causing a console lockup)
   7 * due bh_mask_count not atomic handling. Copyright (C) 1998  Andrea Arcangeli
   8 *
   9 * Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903)
  10 */
  11
  12#include <linux/config.h>
  13#include <linux/mm.h>
  14#include <linux/kernel_stat.h>
  15#include <linux/interrupt.h>
  16#include <linux/smp_lock.h>
  17#include <linux/init.h>
  18#include <linux/tqueue.h>
  19#include <linux/percpu.h>
  20#include <linux/notifier.h>
  21
  22/*
  23   - No shared variables, all the data are CPU local.
  24   - If a softirq needs serialization, let it serialize itself
  25     by its own spinlocks.
  26   - Even if softirq is serialized, only local cpu is marked for
  27     execution. Hence, we get something sort of weak cpu binding.
  28     Though it is still not clear, will it result in better locality
  29     or will not.
  30
  31   Examples:
  32   - NET RX softirq. It is multithreaded and does not require
  33     any global serialization.
  34   - NET TX softirq. It kicks software netdevice queues, hence
  35     it is logically serialized per device, but this serialization
  36     is invisible to common code.
  37   - Tasklets: serialized wrt itself.
  38   - Bottom halves: globally serialized, grr...
  39 */
  40
  41irq_cpustat_t irq_stat[NR_CPUS];
  42
  43static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
  44
  45/*
  46 * we cannot loop indefinitely here to avoid userspace starvation,
  47 * but we also don't want to introduce a worst case 1/HZ latency
  48 * to the pending events, so lets the scheduler to balance
  49 * the softirq load for us.
  50 */
  51static inline void wakeup_softirqd(unsigned cpu)
  52{
  53        struct task_struct * tsk = ksoftirqd_task(cpu);
  54
  55        if (tsk && tsk->state != TASK_RUNNING)
  56                wake_up_process(tsk);
  57}
  58
  59asmlinkage void do_softirq()
  60{
  61        __u32 pending;
  62        unsigned long flags;
  63        __u32 mask;
  64        int cpu;
  65
  66        if (in_interrupt())
  67                return;
  68
  69        local_irq_save(flags);
  70        cpu = smp_processor_id();
  71
  72        pending = softirq_pending(cpu);
  73
  74        if (pending) {
  75                struct softirq_action *h;
  76
  77                mask = ~pending;
  78                local_bh_disable();
  79restart:
  80                /* Reset the pending bitmask before enabling irqs */
  81                softirq_pending(cpu) = 0;
  82
  83                local_irq_enable();
  84
  85                h = softirq_vec;
  86
  87                do {
  88                        if (pending & 1)
  89                                h->action(h);
  90                        h++;
  91                        pending >>= 1;
  92                } while (pending);
  93
  94                local_irq_disable();
  95
  96                pending = softirq_pending(cpu);
  97                if (pending & mask) {
  98                        mask &= ~pending;
  99                        goto restart;
 100                }
 101                __local_bh_enable();
 102
 103                if (pending)
 104                        wakeup_softirqd(cpu);
 105        }
 106
 107        local_irq_restore(flags);
 108}
 109
 110/*
 111 * This function must run with irqs disabled!
 112 */
 113inline void cpu_raise_softirq(unsigned int cpu, unsigned int nr)
 114{
 115        __cpu_raise_softirq(cpu, nr);
 116
 117        /*
 118         * If we're in an interrupt or bh, we're done
 119         * (this also catches bh-disabled code). We will
 120         * actually run the softirq once we return from
 121         * the irq or bh.
 122         *
 123         * Otherwise we wake up ksoftirqd to make sure we
 124         * schedule the softirq soon.
 125         */
 126        if (!in_interrupt())
 127                wakeup_softirqd(cpu);
 128}
 129
 130void raise_softirq(unsigned int nr)
 131{
 132        unsigned long flags;
 133
 134        local_irq_save(flags);
 135        cpu_raise_softirq(smp_processor_id(), nr);
 136        local_irq_restore(flags);
 137}
 138
 139void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
 140{
 141        softirq_vec[nr].data = data;
 142        softirq_vec[nr].action = action;
 143}
 144
 145
 146/* Tasklets */
 147struct tasklet_head
 148{
 149        struct tasklet_struct *list;
 150};
 151
 152/* Some compilers disobey section attribute on statics when not
 153   initialized -- RR */
 154static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
 155static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL };
 156
 157void __tasklet_schedule(struct tasklet_struct *t)
 158{
 159        unsigned long flags;
 160
 161        local_irq_save(flags);
 162        t->next = __get_cpu_var(tasklet_vec).list;
 163        __get_cpu_var(tasklet_vec).list = t;
 164        cpu_raise_softirq(smp_processor_id(), TASKLET_SOFTIRQ);
 165        local_irq_restore(flags);
 166}
 167
 168void __tasklet_hi_schedule(struct tasklet_struct *t)
 169{
 170        unsigned long flags;
 171
 172        local_irq_save(flags);
 173        t->next = __get_cpu_var(tasklet_hi_vec).list;
 174        __get_cpu_var(tasklet_hi_vec).list = t;
 175        cpu_raise_softirq(smp_processor_id(), HI_SOFTIRQ);
 176        local_irq_restore(flags);
 177}
 178
 179static void tasklet_action(struct softirq_action *a)
 180{
 181        struct tasklet_struct *list;
 182
 183        local_irq_disable();
 184        list = __get_cpu_var(tasklet_vec).list;
 185        __get_cpu_var(tasklet_vec).list = NULL;
 186        local_irq_enable();
 187
 188        while (list) {
 189                struct tasklet_struct *t = list;
 190
 191                list = list->next;
 192
 193                if (tasklet_trylock(t)) {
 194                        if (!atomic_read(&t->count)) {
 195                                if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
 196                                        BUG();
 197                                t->func(t->data);
 198                                tasklet_unlock(t);
 199                                continue;
 200                        }
 201                        tasklet_unlock(t);
 202                }
 203
 204                local_irq_disable();
 205                t->next = __get_cpu_var(tasklet_vec).list;
 206                __get_cpu_var(tasklet_vec).list = t;
 207                __cpu_raise_softirq(smp_processor_id(), TASKLET_SOFTIRQ);
 208                local_irq_enable();
 209        }
 210}
 211
 212static void tasklet_hi_action(struct softirq_action *a)
 213{
 214        struct tasklet_struct *list;
 215
 216        local_irq_disable();
 217        list = __get_cpu_var(tasklet_hi_vec).list;
 218        __get_cpu_var(tasklet_hi_vec).list = NULL;
 219        local_irq_enable();
 220
 221        while (list) {
 222                struct tasklet_struct *t = list;
 223
 224                list = list->next;
 225
 226                if (tasklet_trylock(t)) {
 227                        if (!atomic_read(&t->count)) {
 228                                if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
 229                                        BUG();
 230                                t->func(t->data);
 231                                tasklet_unlock(t);
 232                                continue;
 233                        }
 234                        tasklet_unlock(t);
 235                }
 236
 237                local_irq_disable();
 238                t->next = __get_cpu_var(tasklet_hi_vec).list;
 239                __get_cpu_var(tasklet_hi_vec).list = t;
 240                __cpu_raise_softirq(smp_processor_id(), HI_SOFTIRQ);
 241                local_irq_enable();
 242        }
 243}
 244
 245
 246void tasklet_init(struct tasklet_struct *t,
 247                  void (*func)(unsigned long), unsigned long data)
 248{
 249        t->next = NULL;
 250        t->state = 0;
 251        atomic_set(&t->count, 0);
 252        t->func = func;
 253        t->data = data;
 254}
 255
 256void tasklet_kill(struct tasklet_struct *t)
 257{
 258        if (in_interrupt())
 259                printk("Attempt to kill tasklet from interrupt\n");
 260
 261        while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
 262                do
 263                        yield();
 264                while (test_bit(TASKLET_STATE_SCHED, &t->state));
 265        }
 266        tasklet_unlock_wait(t);
 267        clear_bit(TASKLET_STATE_SCHED, &t->state);
 268}
 269
 270
 271
 272/* Old style BHs */
 273
 274static void (*bh_base[32])(void);
 275struct tasklet_struct bh_task_vec[32];
 276
 277/* BHs are serialized by spinlock global_bh_lock.
 278
 279   It is still possible to make synchronize_bh() as
 280   spin_unlock_wait(&global_bh_lock). This operation is not used
 281   by kernel now, so that this lock is not made private only
 282   due to wait_on_irq().
 283
 284   It can be removed only after auditing all the BHs.
 285 */
 286spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED;
 287
 288static void bh_action(unsigned long nr)
 289{
 290        if (!spin_trylock(&global_bh_lock))
 291                goto resched;
 292
 293        if (bh_base[nr])
 294                bh_base[nr]();
 295
 296        hardirq_endlock();
 297        spin_unlock(&global_bh_lock);
 298        return;
 299
 300        spin_unlock(&global_bh_lock);
 301resched:
 302        mark_bh(nr);
 303}
 304
 305void init_bh(int nr, void (*routine)(void))
 306{
 307        bh_base[nr] = routine;
 308        mb();
 309}
 310
 311void remove_bh(int nr)
 312{
 313        tasklet_kill(bh_task_vec+nr);
 314        bh_base[nr] = NULL;
 315}
 316
 317void __init softirq_init()
 318{
 319        int i;
 320
 321        for (i=0; i<32; i++)
 322                tasklet_init(bh_task_vec+i, bh_action, i);
 323
 324        open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
 325        open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
 326}
 327
 328void __run_task_queue(task_queue *list)
 329{
 330        struct list_head head, *next;
 331        unsigned long flags;
 332
 333        spin_lock_irqsave(&tqueue_lock, flags);
 334        list_add(&head, list);
 335        list_del_init(list);
 336        spin_unlock_irqrestore(&tqueue_lock, flags);
 337
 338        next = head.next;
 339        while (next != &head) {
 340                void (*f) (void *);
 341                struct tq_struct *p;
 342                void *data;
 343
 344                p = list_entry(next, struct tq_struct, list);
 345                next = next->next;
 346                f = p->routine;
 347                data = p->data;
 348                wmb();
 349                p->sync = 0;
 350                if (f)
 351                        f(data);
 352        }
 353}
 354
 355static int ksoftirqd(void * __bind_cpu)
 356{
 357        int cpu = (int) (long) __bind_cpu;
 358
 359        daemonize();
 360        set_user_nice(current, 19);
 361        current->flags |= PF_IOTHREAD;
 362        sigfillset(&current->blocked);
 363
 364        /* Migrate to the right CPU */
 365        set_cpus_allowed(current, 1UL << cpu);
 366        if (smp_processor_id() != cpu)
 367                BUG();
 368
 369        sprintf(current->comm, "ksoftirqd_CPU%d", cpu);
 370
 371        __set_current_state(TASK_INTERRUPTIBLE);
 372        mb();
 373
 374        ksoftirqd_task(cpu) = current;
 375
 376        for (;;) {
 377                if (!softirq_pending(cpu))
 378                        schedule();
 379
 380                __set_current_state(TASK_RUNNING);
 381
 382                while (softirq_pending(cpu)) {
 383                        do_softirq();
 384                        cond_resched();
 385                }
 386
 387                __set_current_state(TASK_INTERRUPTIBLE);
 388        }
 389}
 390
 391static int __devinit cpu_callback(struct notifier_block *nfb,
 392                                  unsigned long action,
 393                                  void *hcpu)
 394{
 395        int hotcpu = (unsigned long)hcpu;
 396
 397        if (action == CPU_ONLINE) {
 398                if (kernel_thread(ksoftirqd, hcpu, CLONE_KERNEL) < 0) {
 399                        printk("ksoftirqd for %i failed\n", hotcpu);
 400                        return NOTIFY_BAD;
 401                }
 402
 403                while (!ksoftirqd_task(hotcpu))
 404                        yield();
 405                return NOTIFY_OK;
 406        }
 407        return NOTIFY_BAD;
 408}
 409
 410static struct notifier_block cpu_nfb = { &cpu_callback, NULL, 0 };
 411
 412__init int spawn_ksoftirqd(void)
 413{
 414        cpu_callback(&cpu_nfb, CPU_ONLINE, (void *)(long)smp_processor_id());
 415        register_cpu_notifier(&cpu_nfb);
 416        return 0;
 417}
 418
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.