Merge tag 'smp-core-2020-03-30' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / kernel / cpu.c
index 221bf6a..2371292 100644 (file)
@@ -1041,7 +1041,7 @@ static int cpu_down_maps_locked(unsigned int cpu, enum cpuhp_state target)
        return _cpu_down(cpu, 0, target);
 }
 
-static int do_cpu_down(unsigned int cpu, enum cpuhp_state target)
+static int cpu_down(unsigned int cpu, enum cpuhp_state target)
 {
        int err;
 
@@ -1051,11 +1051,72 @@ static int do_cpu_down(unsigned int cpu, enum cpuhp_state target)
        return err;
 }
 
-int cpu_down(unsigned int cpu)
+/**
+ * cpu_device_down - Bring down a cpu device
+ * @dev: Pointer to the cpu device to offline
+ *
+ * This function is meant to be used by device core cpu subsystem only.
+ *
+ * Other subsystems should use remove_cpu() instead.
+ */
+int cpu_device_down(struct device *dev)
 {
-       return do_cpu_down(cpu, CPUHP_OFFLINE);
+       return cpu_down(dev->id, CPUHP_OFFLINE);
+}
+
+int remove_cpu(unsigned int cpu)
+{
+       int ret;
+
+       lock_device_hotplug();
+       ret = device_offline(get_cpu_device(cpu));
+       unlock_device_hotplug();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(remove_cpu);
+
+void smp_shutdown_nonboot_cpus(unsigned int primary_cpu)
+{
+       unsigned int cpu;
+       int error;
+
+       cpu_maps_update_begin();
+
+       /*
+        * Make certain the cpu I'm about to reboot on is online.
+        *
+        * This is inline to what migrate_to_reboot_cpu() already do.
+        */
+       if (!cpu_online(primary_cpu))
+               primary_cpu = cpumask_first(cpu_online_mask);
+
+       for_each_online_cpu(cpu) {
+               if (cpu == primary_cpu)
+                       continue;
+
+               error = cpu_down_maps_locked(cpu, CPUHP_OFFLINE);
+               if (error) {
+                       pr_err("Failed to offline CPU%d - error=%d",
+                               cpu, error);
+                       break;
+               }
+       }
+
+       /*
+        * Ensure all but the reboot CPU are offline.
+        */
+       BUG_ON(num_online_cpus() > 1);
+
+       /*
+        * Make sure the CPUs won't be enabled by someone else after this
+        * point. Kexec will reboot to a new kernel shortly resetting
+        * everything along the way.
+        */
+       cpu_hotplug_disabled++;
+
+       cpu_maps_update_done();
 }
-EXPORT_SYMBOL(cpu_down);
 
 #else
 #define takedown_cpu           NULL
@@ -1124,8 +1185,8 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
        }
 
        /*
-        * The caller of do_cpu_up might have raced with another
-        * caller. Ignore it for now.
+        * The caller of cpu_up() might have raced with another
+        * caller. Nothing to do.
         */
        if (st->state >= target)
                goto out;
@@ -1169,7 +1230,7 @@ out:
        return ret;
 }
 
-static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
+static int cpu_up(unsigned int cpu, enum cpuhp_state target)
 {
        int err = 0;
 
@@ -1203,16 +1264,70 @@ out:
        return err;
 }
 
-int cpu_up(unsigned int cpu)
+/**
+ * cpu_device_up - Bring up a cpu device
+ * @dev: Pointer to the cpu device to online
+ *
+ * This function is meant to be used by device core cpu subsystem only.
+ *
+ * Other subsystems should use add_cpu() instead.
+ */
+int cpu_device_up(struct device *dev)
+{
+       return cpu_up(dev->id, CPUHP_ONLINE);
+}
+
+int add_cpu(unsigned int cpu)
+{
+       int ret;
+
+       lock_device_hotplug();
+       ret = device_online(get_cpu_device(cpu));
+       unlock_device_hotplug();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(add_cpu);
+
+/**
+ * bringup_hibernate_cpu - Bring up the CPU that we hibernated on
+ * @sleep_cpu: The cpu we hibernated on and should be brought up.
+ *
+ * On some architectures like arm64, we can hibernate on any CPU, but on
+ * wake up the CPU we hibernated on might be offline as a side effect of
+ * using maxcpus= for example.
+ */
+int bringup_hibernate_cpu(unsigned int sleep_cpu)
 {
-       return do_cpu_up(cpu, CPUHP_ONLINE);
+       int ret;
+
+       if (!cpu_online(sleep_cpu)) {
+               pr_info("Hibernated on a CPU that is offline! Bringing CPU up.\n");
+               ret = cpu_up(sleep_cpu, CPUHP_ONLINE);
+               if (ret) {
+                       pr_err("Failed to bring hibernate-CPU up!\n");
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+void bringup_nonboot_cpus(unsigned int setup_max_cpus)
+{
+       unsigned int cpu;
+
+       for_each_present_cpu(cpu) {
+               if (num_online_cpus() >= setup_max_cpus)
+                       break;
+               if (!cpu_online(cpu))
+                       cpu_up(cpu, CPUHP_ONLINE);
+       }
 }
-EXPORT_SYMBOL_GPL(cpu_up);
 
 #ifdef CONFIG_PM_SLEEP_SMP
 static cpumask_var_t frozen_cpus;
 
-int freeze_secondary_cpus(int primary)
+int __freeze_secondary_cpus(int primary, bool suspend)
 {
        int cpu, error = 0;
 
@@ -1237,7 +1352,7 @@ int freeze_secondary_cpus(int primary)
                if (cpu == primary)
                        continue;
 
-               if (pm_wakeup_pending()) {
+               if (suspend && pm_wakeup_pending()) {
                        pr_info("Wakeup pending. Abort CPU freeze\n");
                        error = -EBUSY;
                        break;
@@ -2028,9 +2143,9 @@ static ssize_t write_cpuhp_target(struct device *dev,
                goto out;
 
        if (st->state < target)
-               ret = do_cpu_up(dev->id, target);
+               ret = cpu_up(dev->id, target);
        else
-               ret = do_cpu_down(dev->id, target);
+               ret = cpu_down(dev->id, target);
 out:
        unlock_device_hotplug();
        return ret ? ret : count;