* * schedule_on_each_cpu() is very slow. */int schedule_on_each_cpu(void (*func)(void *info), void *info){ int cpu; struct work_struct *works; works = alloc_percpu(struct work_struct); if (!works) return -ENOMEM; mutex_lock(&workqueue_mutex); for_each_online_cpu(cpu) { INIT_WORK(per_cpu_ptr(works, cpu), func, info); __queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), per_cpu_ptr(works, cpu)); } mutex_unlock(&workqueue_mutex); flush_workqueue(keventd_wq); free_percpu(works); return 0;} void flush_scheduled_work(void){ flush_workqueue(keventd_wq);}EXPORT_SYMBOL(flush_scheduled_work); /** * cancel_rearming_delayed_workqueue - reliably kill off a delayed * work whose handler rearms the delayed work. * @wq: the controlling workqueue structure * @work: the delayed work struct */void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, struct work_struct *work){ while (!cancel_delayed_work(work)) flush_workqueue(wq);}EXPORT_SYMBOL(cancel_rearming_delayed_workqueue); /** * cancel_rearming_delayed_work - reliably kill off a delayed keventd * work whose handler rearms the delayed work. * @work: the delayed work struct */void cancel_rearming_delayed_work(struct work_struct *work){ cancel_rearming_delayed_workqueue(keventd_wq, work);}EXPORT_SYMBOL(cancel_rearming_delayed_work); /** * execute_in_process_context - reliably execute the routine with user context * @fn: the function to execute * @data: data to pass to the function * @ew: guaranteed storage for the execute work structure (must * be available when the work executes) * * Executes the function immediately if process context is available, * otherwise schedules the function for delayed execution. * * Returns: 0 - function was executed * 1 - function was scheduled for execution */int execute_in_process_context(void (*fn)(void *data), void *data, struct execute_work *ew){ if (!in_interrupt()) { fn(data); return 0; } INIT_WORK(&ew->work, fn, data); schedule_work(&ew->work); return 1;}EXPORT_SYMBOL_GPL(execute_in_process_context); int keventd_up(void){ return keventd_wq != NULL;} int current_is_keventd(void){ struct cpu_workqueue_struct *cwq; int cpu = smp_processor_id(); /* preempt-safe: keventd is per-cpu */ int ret = 0; BUG_ON(!keventd_wq); cwq = per_cpu_ptr(keventd_wq->cpu_wq, cpu); if (current == cwq->thread) ret = 1; return ret; } #ifdef CONFIG_HOTPLUG_CPU/* Take the work from this (downed) CPU. */static void take_over_work(struct workqueue_struct *wq, unsigned int cpu){ struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); struct list_head list; struct work_struct *work; spin_lock_irq(&cwq->lock); list_replace_init(&cwq->worklist, &list); while (!list_empty(&list)) { printk("Taking work for %s\n", wq->name); work = list_entry(list.next,struct work_struct,entry); list_del(&work->entry); __queue_work(per_cpu_ptr(wq->cpu_wq, smp_processor_id()), work); } spin_unlock_irq(&cwq->lock);} /* We're holding the cpucontrol mutex here */static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu){ unsigned int hotcpu = (unsigned long)hcpu; struct workqueue_struct *wq; switch (action) { case CPU_UP_PREPARE: mutex_lock(&workqueue_mutex); /* Create a new workqueue thread for it. */ list_for_each_entry(wq, &workqueues, list) { if (!create_workqueue_thread(wq, hotcpu)) { printk("workqueue for %i failed\n", hotcpu); return NOTIFY_BAD; } } break; case CPU_ONLINE: /* Kick off worker threads. */ list_for_each_entry(wq, &workqueues, list) { struct cpu_workqueue_struct *cwq; cwq = per_cpu_ptr(wq->cpu_wq, hotcpu); kthread_bind(cwq->thread, hotcpu); wake_up_process(cwq->thread); } mutex_unlock(&workqueue_mutex); break; case CPU_UP_CANCELED: list_for_each_entry(wq, &workqueues, list) { if (!per_cpu_ptr(wq->cpu_wq, hotcpu)->thread) continue; /* Unbind so it can run. */ kthread_bind(per_cpu_ptr(wq->cpu_wq, hotcpu)->thread, any_online_cpu(cpu_online_map)); cleanup_workqueue_thread(wq, hotcpu); } mutex_unlock(&workqueue_mutex); break; case CPU_DOWN_PREPARE: mutex_lock(&workqueue_mutex); break; case CPU_DOWN_FAILED: mutex_unlock(&workqueue_mutex); break; case CPU_DEAD: list_for_each_entry(wq, &workqueues, list) cleanup_workqueue_thread(wq, hotcpu); list_for_each_entry(wq, &workqueues, list) take_over_work(wq, hotcpu); mutex_unlock(&workqueue_mutex); break; } return NOTIFY_OK;}#endif void init_workqueues(void){ singlethread_cpu = first_cpu(cpu_possible_map); hotcpu_notifier(workqueue_cpu_callback, 0); keventd_wq = create_workqueue("events"); BUG_ON(!keventd_wq);} Lines 501–697 of 697. Previous lines
* * schedule_on_each_cpu() is very slow. */ int schedule_on_each_cpu(void (*func)(void *info), void *info) { int cpu; struct work_struct *works; works = alloc_percpu(struct work_struct); if (!works) return -ENOMEM; mutex_lock(&workqueue_mutex); for_each_online_cpu(cpu) { INIT_WORK(per_cpu_ptr(works, cpu), func, info); __queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), per_cpu_ptr(works, cpu)); } mutex_unlock(&workqueue_mutex); flush_workqueue(keventd_wq); free_percpu(works); return 0; } void flush_scheduled_work(void) { flush_workqueue(keventd_wq); } EXPORT_SYMBOL(flush_scheduled_work); /** * cancel_rearming_delayed_workqueue - reliably kill off a delayed * work whose handler rearms the delayed work. * @wq: the controlling workqueue structure * @work: the delayed work struct */ void cancel_rearming_delayed_workqueue(struct workqueue_struct *wq, struct work_struct *work) { while (!cancel_delayed_work(work)) flush_workqueue(wq); } EXPORT_SYMBOL(cancel_rearming_delayed_workqueue); /** * cancel_rearming_delayed_work - reliably kill off a delayed keventd * work whose handler rearms the delayed work. * @work: the delayed work struct */ void cancel_rearming_delayed_work(struct work_struct *work) { cancel_rearming_delayed_workqueue(keventd_wq, work); } EXPORT_SYMBOL(cancel_rearming_delayed_work); /** * execute_in_process_context - reliably execute the routine with user context * @fn: the function to execute * @data: data to pass to the function * @ew: guaranteed storage for the execute work structure (must * be available when the work executes) * * Executes the function immediately if process context is available, * otherwise schedules the function for delayed execution. * * Returns: 0 - function was executed * 1 - function was scheduled for execution */ int execute_in_process_context(void (*fn)(void *data), void *data, struct execute_work *ew) { if (!in_interrupt()) { fn(data); return 0; } INIT_WORK(&ew->work, fn, data); schedule_work(&ew->work); return 1; } EXPORT_SYMBOL_GPL(execute_in_process_context); int keventd_up(void) { return keventd_wq != NULL; } int current_is_keventd(void) { struct cpu_workqueue_struct *cwq; int cpu = smp_processor_id(); /* preempt-safe: keventd is per-cpu */ int ret = 0; BUG_ON(!keventd_wq); cwq = per_cpu_ptr(keventd_wq->cpu_wq, cpu); if (current == cwq->thread) ret = 1; return ret; } #ifdef CONFIG_HOTPLUG_CPU /* Take the work from this (downed) CPU. */ static void take_over_work(struct workqueue_struct *wq, unsigned int cpu) { struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); struct list_head list; struct work_struct *work; spin_lock_irq(&cwq->lock); list_replace_init(&cwq->worklist, &list); while (!list_empty(&list)) { printk("Taking work for %s\n", wq->name); work = list_entry(list.next,struct work_struct,entry); list_del(&work->entry); __queue_work(per_cpu_ptr(wq->cpu_wq, smp_processor_id()), work); } spin_unlock_irq(&cwq->lock); } /* We're holding the cpucontrol mutex here */ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int hotcpu = (unsigned long)hcpu; struct workqueue_struct *wq; switch (action) { case CPU_UP_PREPARE: mutex_lock(&workqueue_mutex); /* Create a new workqueue thread for it. */ list_for_each_entry(wq, &workqueues, list) { if (!create_workqueue_thread(wq, hotcpu)) { printk("workqueue for %i failed\n", hotcpu); return NOTIFY_BAD; } } break; case CPU_ONLINE: /* Kick off worker threads. */ list_for_each_entry(wq, &workqueues, list) { struct cpu_workqueue_struct *cwq; cwq = per_cpu_ptr(wq->cpu_wq, hotcpu); kthread_bind(cwq->thread, hotcpu); wake_up_process(cwq->thread); } mutex_unlock(&workqueue_mutex); break; case CPU_UP_CANCELED: list_for_each_entry(wq, &workqueues, list) { if (!per_cpu_ptr(wq->cpu_wq, hotcpu)->thread) continue; /* Unbind so it can run. */ kthread_bind(per_cpu_ptr(wq->cpu_wq, hotcpu)->thread, any_online_cpu(cpu_online_map)); cleanup_workqueue_thread(wq, hotcpu); } mutex_unlock(&workqueue_mutex); break; case CPU_DOWN_PREPARE: mutex_lock(&workqueue_mutex); break; case CPU_DOWN_FAILED: mutex_unlock(&workqueue_mutex); break; case CPU_DEAD: list_for_each_entry(wq, &workqueues, list) cleanup_workqueue_thread(wq, hotcpu); list_for_each_entry(wq, &workqueues, list) take_over_work(wq, hotcpu); mutex_unlock(&workqueue_mutex); break; } return NOTIFY_OK; } #endif void init_workqueues(void) { singlethread_cpu = first_cpu(cpu_possible_map); hotcpu_notifier(workqueue_cpu_callback, 0); keventd_wq = create_workqueue("events"); BUG_ON(!keventd_wq); }