Merge tag 'mips_5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
[linux-2.6-microblaze.git] / kernel / kthread.c
index 933a625..a5eceec 100644 (file)
@@ -704,8 +704,15 @@ repeat:
        raw_spin_unlock_irq(&worker->lock);
 
        if (work) {
+               kthread_work_func_t func = work->func;
                __set_current_state(TASK_RUNNING);
+               trace_sched_kthread_work_execute_start(work);
                work->func(work);
+               /*
+                * Avoid dereferencing work after this point.  The trace
+                * event only cares about the address.
+                */
+               trace_sched_kthread_work_execute_end(work, func);
        } else if (!freezing(current))
                schedule();
 
@@ -786,7 +793,25 @@ EXPORT_SYMBOL(kthread_create_worker);
  * A good practice is to add the cpu number also into the worker name.
  * For example, use kthread_create_worker_on_cpu(cpu, "helper/%d", cpu).
  *
- * Returns a pointer to the allocated worker on success, ERR_PTR(-ENOMEM)
+ * CPU hotplug:
+ * The kthread worker API is simple and generic. It just provides a way
+ * to create, use, and destroy workers.
+ *
+ * It is up to the API user how to handle CPU hotplug. They have to decide
+ * how to handle pending work items, prevent queuing new ones, and
+ * restore the functionality when the CPU goes off and on. There are a
+ * few catches:
+ *
+ *    - CPU affinity gets lost when it is scheduled on an offline CPU.
+ *
+ *    - The worker might not exist when the CPU was off when the user
+ *      created the workers.
+ *
+ * Good practice is to implement two CPU hotplug callbacks and to
+ * destroy/create the worker when the CPU goes down/up.
+ *
+ * Return:
+ * The pointer to the allocated worker on success, ERR_PTR(-ENOMEM)
  * when the needed structures could not get allocated, and ERR_PTR(-EINTR)
  * when the worker was SIGKILLed.
  */
@@ -834,6 +859,8 @@ static void kthread_insert_work(struct kthread_worker *worker,
 {
        kthread_insert_work_sanity_check(worker, work);
 
+       trace_sched_kthread_work_queue_work(worker, work);
+
        list_add_tail(&work->node, pos);
        work->worker = worker;
        if (!worker->current_work && likely(worker->task))
@@ -1249,6 +1276,7 @@ void kthread_use_mm(struct mm_struct *mm)
                tsk->active_mm = mm;
        }
        tsk->mm = mm;
+       membarrier_update_current_mm(mm);
        switch_mm_irqs_off(active_mm, mm, tsk);
        local_irq_enable();
        task_unlock(tsk);
@@ -1256,8 +1284,19 @@ void kthread_use_mm(struct mm_struct *mm)
        finish_arch_post_lock_switch();
 #endif
 
+       /*
+        * When a kthread starts operating on an address space, the loop
+        * in membarrier_{private,global}_expedited() may not observe
+        * that tsk->mm, and not issue an IPI. Membarrier requires a
+        * memory barrier after storing to tsk->mm, before accessing
+        * user-space memory. A full memory barrier for membarrier
+        * {PRIVATE,GLOBAL}_EXPEDITED is implicitly provided by
+        * mmdrop(), or explicitly with smp_mb().
+        */
        if (active_mm != mm)
                mmdrop(active_mm);
+       else
+               smp_mb();
 
        to_kthread(tsk)->oldfs = force_uaccess_begin();
 }
@@ -1277,9 +1316,18 @@ void kthread_unuse_mm(struct mm_struct *mm)
        force_uaccess_end(to_kthread(tsk)->oldfs);
 
        task_lock(tsk);
+       /*
+        * When a kthread stops operating on an address space, the loop
+        * in membarrier_{private,global}_expedited() may not observe
+        * that tsk->mm, and not issue an IPI. Membarrier requires a
+        * memory barrier after accessing user-space memory, before
+        * clearing tsk->mm.
+        */
+       smp_mb__after_spinlock();
        sync_mm_rss(mm);
        local_irq_disable();
        tsk->mm = NULL;
+       membarrier_update_current_mm(NULL);
        /* active_mm is still 'mm' */
        enter_lazy_tlb(mm, tsk);
        local_irq_enable();