]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/cpu.c
bpf: fix bpf_trace_printk on 32 bit archs
[karo-tx-linux.git] / kernel / cpu.c
index cb5103413bd8df5056b3eda682a8d93b08656dd8..eee0331342628f09a02720ffe34aba2f8fb12f31 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/smpboot.h>
 #include <linux/relay.h>
 #include <linux/slab.h>
+#include <linux/percpu-rwsem.h>
 
 #include <trace/events/power.h>
 #define CREATE_TRACE_POINTS
@@ -65,6 +66,12 @@ struct cpuhp_cpu_state {
 
 static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state);
 
+#if defined(CONFIG_LOCKDEP) && defined(CONFIG_SMP)
+static struct lock_class_key cpuhp_state_key;
+static struct lockdep_map cpuhp_state_lock_map =
+       STATIC_LOCKDEP_MAP_INIT("cpuhp_state", &cpuhp_state_key);
+#endif
+
 /**
  * cpuhp_step - Hotplug state machine step
  * @name:      Name of the step
@@ -196,121 +203,41 @@ void cpu_maps_update_done(void)
        mutex_unlock(&cpu_add_remove_lock);
 }
 
-/* If set, cpu_up and cpu_down will return -EBUSY and do nothing.
+/*
+ * If set, cpu_up and cpu_down will return -EBUSY and do nothing.
  * Should always be manipulated under cpu_add_remove_lock
  */
 static int cpu_hotplug_disabled;
 
 #ifdef CONFIG_HOTPLUG_CPU
 
-static struct {
-       struct task_struct *active_writer;
-       /* wait queue to wake up the active_writer */
-       wait_queue_head_t wq;
-       /* verifies that no writer will get active while readers are active */
-       struct mutex lock;
-       /*
-        * Also blocks the new readers during
-        * an ongoing cpu hotplug operation.
-        */
-       atomic_t refcount;
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-       struct lockdep_map dep_map;
-#endif
-} cpu_hotplug = {
-       .active_writer = NULL,
-       .wq = __WAIT_QUEUE_HEAD_INITIALIZER(cpu_hotplug.wq),
-       .lock = __MUTEX_INITIALIZER(cpu_hotplug.lock),
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-       .dep_map = STATIC_LOCKDEP_MAP_INIT("cpu_hotplug.dep_map", &cpu_hotplug.dep_map),
-#endif
-};
+DEFINE_STATIC_PERCPU_RWSEM(cpu_hotplug_lock);
 
-/* Lockdep annotations for get/put_online_cpus() and cpu_hotplug_begin/end() */
-#define cpuhp_lock_acquire_read() lock_map_acquire_read(&cpu_hotplug.dep_map)
-#define cpuhp_lock_acquire_tryread() \
-                                 lock_map_acquire_tryread(&cpu_hotplug.dep_map)
-#define cpuhp_lock_acquire()      lock_map_acquire(&cpu_hotplug.dep_map)
-#define cpuhp_lock_release()      lock_map_release(&cpu_hotplug.dep_map)
-
-
-void get_online_cpus(void)
+void cpus_read_lock(void)
 {
-       might_sleep();
-       if (cpu_hotplug.active_writer == current)
-               return;
-       cpuhp_lock_acquire_read();
-       mutex_lock(&cpu_hotplug.lock);
-       atomic_inc(&cpu_hotplug.refcount);
-       mutex_unlock(&cpu_hotplug.lock);
+       percpu_down_read(&cpu_hotplug_lock);
 }
-EXPORT_SYMBOL_GPL(get_online_cpus);
+EXPORT_SYMBOL_GPL(cpus_read_lock);
 
-void put_online_cpus(void)
+void cpus_read_unlock(void)
 {
-       int refcount;
-
-       if (cpu_hotplug.active_writer == current)
-               return;
-
-       refcount = atomic_dec_return(&cpu_hotplug.refcount);
-       if (WARN_ON(refcount < 0)) /* try to fix things up */
-               atomic_inc(&cpu_hotplug.refcount);
-
-       if (refcount <= 0 && waitqueue_active(&cpu_hotplug.wq))
-               wake_up(&cpu_hotplug.wq);
-
-       cpuhp_lock_release();
-
+       percpu_up_read(&cpu_hotplug_lock);
 }
-EXPORT_SYMBOL_GPL(put_online_cpus);
+EXPORT_SYMBOL_GPL(cpus_read_unlock);
 
-/*
- * This ensures that the hotplug operation can begin only when the
- * refcount goes to zero.
- *
- * Note that during a cpu-hotplug operation, the new readers, if any,
- * will be blocked by the cpu_hotplug.lock
- *
- * Since cpu_hotplug_begin() is always called after invoking
- * cpu_maps_update_begin(), we can be sure that only one writer is active.
- *
- * Note that theoretically, there is a possibility of a livelock:
- * - Refcount goes to zero, last reader wakes up the sleeping
- *   writer.
- * - Last reader unlocks the cpu_hotplug.lock.
- * - A new reader arrives at this moment, bumps up the refcount.
- * - The writer acquires the cpu_hotplug.lock finds the refcount
- *   non zero and goes to sleep again.
- *
- * However, this is very difficult to achieve in practice since
- * get_online_cpus() not an api which is called all that often.
- *
- */
-void cpu_hotplug_begin(void)
+void cpus_write_lock(void)
 {
-       DEFINE_WAIT(wait);
-
-       cpu_hotplug.active_writer = current;
-       cpuhp_lock_acquire();
+       percpu_down_write(&cpu_hotplug_lock);
+}
 
-       for (;;) {
-               mutex_lock(&cpu_hotplug.lock);
-               prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE);
-               if (likely(!atomic_read(&cpu_hotplug.refcount)))
-                               break;
-               mutex_unlock(&cpu_hotplug.lock);
-               schedule();
-       }
-       finish_wait(&cpu_hotplug.wq, &wait);
+void cpus_write_unlock(void)
+{
+       percpu_up_write(&cpu_hotplug_lock);
 }
 
-void cpu_hotplug_done(void)
+void lockdep_assert_cpus_held(void)
 {
-       cpu_hotplug.active_writer = NULL;
-       mutex_unlock(&cpu_hotplug.lock);
-       cpuhp_lock_release();
+       percpu_rwsem_assert_held(&cpu_hotplug_lock);
 }
 
 /*
@@ -344,13 +271,26 @@ void cpu_hotplug_enable(void)
 EXPORT_SYMBOL_GPL(cpu_hotplug_enable);
 #endif /* CONFIG_HOTPLUG_CPU */
 
-/* Notifier wrappers for transitioning to state machine */
+static void __cpuhp_kick_ap_work(struct cpuhp_cpu_state *st);
 
 static int bringup_wait_for_ap(unsigned int cpu)
 {
        struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
 
+       /* Wait for the CPU to reach CPUHP_AP_ONLINE_IDLE */
        wait_for_completion(&st->done);
+       if (WARN_ON_ONCE((!cpu_online(cpu))))
+               return -ECANCELED;
+
+       /* Unpark the stopper thread and the hotplug thread of the target cpu */
+       stop_machine_unpark(cpu);
+       kthread_unpark(st->thread);
+
+       /* Should we go further up ? */
+       if (st->target > CPUHP_AP_ONLINE_IDLE) {
+               __cpuhp_kick_ap_work(st);
+               wait_for_completion(&st->done);
+       }
        return st->result;
 }
 
@@ -371,9 +311,7 @@ static int bringup_cpu(unsigned int cpu)
        irq_unlock_sparse();
        if (ret)
                return ret;
-       ret = bringup_wait_for_ap(cpu);
-       BUG_ON(!cpu_online(cpu));
-       return ret;
+       return bringup_wait_for_ap(cpu);
 }
 
 /*
@@ -484,6 +422,7 @@ static void cpuhp_thread_fun(unsigned int cpu)
 
        st->should_run = false;
 
+       lock_map_acquire(&cpuhp_state_lock_map);
        /* Single callback invocation for [un]install ? */
        if (st->single) {
                if (st->cb_state < CPUHP_AP_ONLINE) {
@@ -510,6 +449,7 @@ static void cpuhp_thread_fun(unsigned int cpu)
                else if (st->state > st->target)
                        ret = cpuhp_ap_offline(cpu, st);
        }
+       lock_map_release(&cpuhp_state_lock_map);
        st->result = ret;
        complete(&st->done);
 }
@@ -524,6 +464,9 @@ cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup,
        if (!cpu_online(cpu))
                return 0;
 
+       lock_map_acquire(&cpuhp_state_lock_map);
+       lock_map_release(&cpuhp_state_lock_map);
+
        /*
         * If we are up and running, use the hotplug thread. For early calls
         * we invoke the thread function directly.
@@ -567,6 +510,8 @@ static int cpuhp_kick_ap_work(unsigned int cpu)
        enum cpuhp_state state = st->state;
 
        trace_cpuhp_enter(cpu, st->target, state, cpuhp_kick_ap_work);
+       lock_map_acquire(&cpuhp_state_lock_map);
+       lock_map_release(&cpuhp_state_lock_map);
        __cpuhp_kick_ap_work(st);
        wait_for_completion(&st->done);
        trace_cpuhp_exit(cpu, st->state, state, st->result);
@@ -630,30 +575,6 @@ void clear_tasks_mm_cpumask(int cpu)
        rcu_read_unlock();
 }
 
-static inline void check_for_tasks(int dead_cpu)
-{
-       struct task_struct *g, *p;
-
-       read_lock(&tasklist_lock);
-       for_each_process_thread(g, p) {
-               if (!p->on_rq)
-                       continue;
-               /*
-                * We do the check with unlocked task_rq(p)->lock.
-                * Order the reading to do not warn about a task,
-                * which was running on this cpu in the past, and
-                * it's just been woken on another cpu.
-                */
-               rmb();
-               if (task_cpu(p) != dead_cpu)
-                       continue;
-
-               pr_warn("Task %s (pid=%d) is on cpu %d (state=%ld, flags=%x)\n",
-                       p->comm, task_pid_nr(p), dead_cpu, p->state, p->flags);
-       }
-       read_unlock(&tasklist_lock);
-}
-
 /* Take this CPU down. */
 static int take_cpu_down(void *_param)
 {
@@ -701,7 +622,7 @@ static int takedown_cpu(unsigned int cpu)
        /*
         * So now all preempt/rcu users must observe !cpu_active().
         */
-       err = stop_machine(take_cpu_down, NULL, cpumask_of(cpu));
+       err = stop_machine_cpuslocked(take_cpu_down, NULL, cpumask_of(cpu));
        if (err) {
                /* CPU refused to die */
                irq_unlock_sparse();
@@ -773,7 +694,7 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen,
        if (!cpu_present(cpu))
                return -EINVAL;
 
-       cpu_hotplug_begin();
+       cpus_write_lock();
 
        cpuhp_tasks_frozen = tasks_frozen;
 
@@ -811,7 +732,7 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen,
        }
 
 out:
-       cpu_hotplug_done();
+       cpus_write_unlock();
        return ret;
 }
 
@@ -859,31 +780,20 @@ void notify_cpu_starting(unsigned int cpu)
 }
 
 /*
- * Called from the idle task. We need to set active here, so we can kick off
- * the stopper thread and unpark the smpboot threads. If the target state is
- * beyond CPUHP_AP_ONLINE_IDLE we kick cpuhp thread and let it bring up the
- * cpu further.
+ * Called from the idle task. Wake up the controlling task which brings the
+ * stopper and the hotplug thread of the upcoming CPU up and then delegates
+ * the rest of the online bringup to the hotplug thread.
  */
 void cpuhp_online_idle(enum cpuhp_state state)
 {
        struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state);
-       unsigned int cpu = smp_processor_id();
 
        /* Happens for the boot cpu */
        if (state != CPUHP_AP_ONLINE_IDLE)
                return;
 
        st->state = CPUHP_AP_ONLINE_IDLE;
-
-       /* Unpark the stopper thread and the hotplug thread of this cpu */
-       stop_machine_unpark(cpu);
-       kthread_unpark(st->thread);
-
-       /* Should we go further up ? */
-       if (st->target > CPUHP_AP_ONLINE_IDLE)
-               __cpuhp_kick_ap_work(st);
-       else
-               complete(&st->done);
+       complete(&st->done);
 }
 
 /* Requires cpu_add_remove_lock to be held */
@@ -893,7 +803,7 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
        struct task_struct *idle;
        int ret = 0;
 
-       cpu_hotplug_begin();
+       cpus_write_lock();
 
        if (!cpu_present(cpu)) {
                ret = -EINVAL;
@@ -941,7 +851,7 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
        target = min((int)target, CPUHP_BRINGUP_CPU);
        ret = cpuhp_up_callbacks(cpu, st, target);
 out:
-       cpu_hotplug_done();
+       cpus_write_unlock();
        return ret;
 }
 
@@ -1252,6 +1162,11 @@ static struct cpuhp_step cpuhp_ap_states[] = {
                .startup.single         = smpboot_unpark_threads,
                .teardown.single        = NULL,
        },
+       [CPUHP_AP_IRQ_AFFINITY_ONLINE] = {
+               .name                   = "irq/affinity:online",
+               .startup.single         = irq_affinity_online_cpu,
+               .teardown.single        = NULL,
+       },
        [CPUHP_AP_PERF_ONLINE] = {
                .name                   = "perf:online",
                .startup.single         = perf_event_init_cpu,
@@ -1413,18 +1328,20 @@ static void cpuhp_rollback_install(int failedcpu, enum cpuhp_state state,
        }
 }
 
-int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
-                              bool invoke)
+int __cpuhp_state_add_instance_cpuslocked(enum cpuhp_state state,
+                                         struct hlist_node *node,
+                                         bool invoke)
 {
        struct cpuhp_step *sp;
        int cpu;
        int ret;
 
+       lockdep_assert_cpus_held();
+
        sp = cpuhp_get_step(state);
        if (sp->multi_instance == false)
                return -EINVAL;
 
-       get_online_cpus();
        mutex_lock(&cpuhp_state_mutex);
 
        if (!invoke || !sp->startup.multi)
@@ -1453,13 +1370,23 @@ add_node:
        hlist_add_head(node, &sp->list);
 unlock:
        mutex_unlock(&cpuhp_state_mutex);
-       put_online_cpus();
+       return ret;
+}
+
+int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
+                              bool invoke)
+{
+       int ret;
+
+       cpus_read_lock();
+       ret = __cpuhp_state_add_instance_cpuslocked(state, node, invoke);
+       cpus_read_unlock();
        return ret;
 }
 EXPORT_SYMBOL_GPL(__cpuhp_state_add_instance);
 
 /**
- * __cpuhp_setup_state - Setup the callbacks for an hotplug machine state
+ * __cpuhp_setup_state_cpuslocked - Setup the callbacks for an hotplug machine state
  * @state:             The state to setup
  * @invoke:            If true, the startup function is invoked for cpus where
  *                     cpu state >= @state
@@ -1468,25 +1395,27 @@ EXPORT_SYMBOL_GPL(__cpuhp_state_add_instance);
  * @multi_instance:    State is set up for multiple instances which get
  *                     added afterwards.
  *
+ * The caller needs to hold cpus read locked while calling this function.
  * Returns:
  *   On success:
  *      Positive state number if @state is CPUHP_AP_ONLINE_DYN
  *      0 for all other states
  *   On failure: proper (negative) error code
  */
-int __cpuhp_setup_state(enum cpuhp_state state,
-                       const char *name, bool invoke,
-                       int (*startup)(unsigned int cpu),
-                       int (*teardown)(unsigned int cpu),
-                       bool multi_instance)
+int __cpuhp_setup_state_cpuslocked(enum cpuhp_state state,
+                                  const char *name, bool invoke,
+                                  int (*startup)(unsigned int cpu),
+                                  int (*teardown)(unsigned int cpu),
+                                  bool multi_instance)
 {
        int cpu, ret = 0;
        bool dynstate;
 
+       lockdep_assert_cpus_held();
+
        if (cpuhp_cb_check(state) || !name)
                return -EINVAL;
 
-       get_online_cpus();
        mutex_lock(&cpuhp_state_mutex);
 
        ret = cpuhp_store_callbacks(state, name, startup, teardown,
@@ -1522,7 +1451,6 @@ int __cpuhp_setup_state(enum cpuhp_state state,
        }
 out:
        mutex_unlock(&cpuhp_state_mutex);
-       put_online_cpus();
        /*
         * If the requested state is CPUHP_AP_ONLINE_DYN, return the
         * dynamically allocated state in case of success.
@@ -1531,6 +1459,22 @@ out:
                return state;
        return ret;
 }
+EXPORT_SYMBOL(__cpuhp_setup_state_cpuslocked);
+
+int __cpuhp_setup_state(enum cpuhp_state state,
+                       const char *name, bool invoke,
+                       int (*startup)(unsigned int cpu),
+                       int (*teardown)(unsigned int cpu),
+                       bool multi_instance)
+{
+       int ret;
+
+       cpus_read_lock();
+       ret = __cpuhp_setup_state_cpuslocked(state, name, invoke, startup,
+                                            teardown, multi_instance);
+       cpus_read_unlock();
+       return ret;
+}
 EXPORT_SYMBOL(__cpuhp_setup_state);
 
 int __cpuhp_state_remove_instance(enum cpuhp_state state,
@@ -1544,7 +1488,7 @@ int __cpuhp_state_remove_instance(enum cpuhp_state state,
        if (!sp->multi_instance)
                return -EINVAL;
 
-       get_online_cpus();
+       cpus_read_lock();
        mutex_lock(&cpuhp_state_mutex);
 
        if (!invoke || !cpuhp_get_teardown_cb(state))
@@ -1565,29 +1509,30 @@ int __cpuhp_state_remove_instance(enum cpuhp_state state,
 remove:
        hlist_del(node);
        mutex_unlock(&cpuhp_state_mutex);
-       put_online_cpus();
+       cpus_read_unlock();
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(__cpuhp_state_remove_instance);
 
 /**
- * __cpuhp_remove_state - Remove the callbacks for an hotplug machine state
+ * __cpuhp_remove_state_cpuslocked - Remove the callbacks for an hotplug machine state
  * @state:     The state to remove
  * @invoke:    If true, the teardown function is invoked for cpus where
  *             cpu state >= @state
  *
+ * The caller needs to hold cpus read locked while calling this function.
  * The teardown callback is currently not allowed to fail. Think
  * about module removal!
  */
-void __cpuhp_remove_state(enum cpuhp_state state, bool invoke)
+void __cpuhp_remove_state_cpuslocked(enum cpuhp_state state, bool invoke)
 {
        struct cpuhp_step *sp = cpuhp_get_step(state);
        int cpu;
 
        BUG_ON(cpuhp_cb_check(state));
 
-       get_online_cpus();
+       lockdep_assert_cpus_held();
 
        mutex_lock(&cpuhp_state_mutex);
        if (sp->multi_instance) {
@@ -1615,7 +1560,14 @@ void __cpuhp_remove_state(enum cpuhp_state state, bool invoke)
 remove:
        cpuhp_store_callbacks(state, NULL, NULL, NULL, false);
        mutex_unlock(&cpuhp_state_mutex);
-       put_online_cpus();
+}
+EXPORT_SYMBOL(__cpuhp_remove_state_cpuslocked);
+
+void __cpuhp_remove_state(enum cpuhp_state state, bool invoke)
+{
+       cpus_read_lock();
+       __cpuhp_remove_state_cpuslocked(state, invoke);
+       cpus_read_unlock();
 }
 EXPORT_SYMBOL(__cpuhp_remove_state);
 
@@ -1684,7 +1636,7 @@ static struct attribute *cpuhp_cpu_attrs[] = {
        NULL
 };
 
-static struct attribute_group cpuhp_cpu_attr_group = {
+static const struct attribute_group cpuhp_cpu_attr_group = {
        .attrs = cpuhp_cpu_attrs,
        .name = "hotplug",
        NULL
@@ -1716,7 +1668,7 @@ static struct attribute *cpuhp_cpu_root_attrs[] = {
        NULL
 };
 
-static struct attribute_group cpuhp_cpu_root_attr_group = {
+static const struct attribute_group cpuhp_cpu_root_attr_group = {
        .attrs = cpuhp_cpu_root_attrs,
        .name = "hotplug",
        NULL