Implement the LUO file handler (cpu_preserve_handler) and early boot
incoming CPU adoption for /sys/devices/system/cpu/cpuX/preserve fds.

Signed-off-by: Pasha Tatashin <[email protected]>
---
 kernel/liveupdate/cpu_preserve.c | 189 ++++++++++++++++++++++++++++++-
 1 file changed, 185 insertions(+), 4 deletions(-)

diff --git a/kernel/liveupdate/cpu_preserve.c b/kernel/liveupdate/cpu_preserve.c
index 2f92dfee82b1..9a039ba9e912 100644
--- a/kernel/liveupdate/cpu_preserve.c
+++ b/kernel/liveupdate/cpu_preserve.c
@@ -837,8 +837,10 @@ void __cpu_preserved_text cpu_preserved_set_dead(int cpu)
 {
        struct cpu_preserved_pcpu_ser *ser = cpu_preserved_get_pcpu_ser(cpu);
 
-       if (ser)
-               WRITE_ONCE(ser->workload, CPU_PRESERVED_DEAD);
+       if (ser) {
+               smp_store_release(&ser->workload, CPU_PRESERVED_DEAD);
+               cpu_preserved_clean(ser);
+       }
 }
 EXPORT_SYMBOL_GPL(cpu_preserved_set_dead);
 
@@ -1097,8 +1099,6 @@ void __cpu_preserved_text cpu_preserved_park_loop(int cpu)
                switch (READ_ONCE(ser->workload)) {
                case CPU_PRESERVED_EXITING:
                case CPU_PRESERVED_DEAD:
-                       WRITE_ONCE(ser->workload, CPU_PRESERVED_DEAD);
-                       cpu_preserved_clean(ser);
                        return;
                case CPU_PRESERVED_WORKLOAD:
                        cpu_preserved_run_workload(ser, pcpu);
@@ -1131,6 +1131,7 @@ void cpu_preserved_park(int cpu)
        } else {
                cpu_preserved_park_loop(cpu);
                arch_cpu_preserved_park_finish(cpu);
+               cpu_preserved_set_dead(cpu);
        }
 }
 EXPORT_SYMBOL_GPL(cpu_preserved_park);
@@ -1498,6 +1499,171 @@ static struct liveupdate_flb cpu_preserved_flb = {
        .compatible = CPU_PRESERVED_LUO_FLB_COMPATIBLE,
 };
 
+/*
+ * LUO File Handler Callbacks for /sys/devices/system/cpu/cpu<N>/preserve
+ */
+static int file_to_cpu(struct file *file, unsigned int *cpup)
+{
+       struct dentry *dentry, *parent;
+       unsigned int cpu;
+
+       if (!file || !file->f_path.dentry)
+               return -EINVAL;
+
+       if (file_inode(file)->i_sb->s_magic != SYSFS_MAGIC)
+               return -EINVAL;
+
+       dentry = file->f_path.dentry;
+       if (strcmp(dentry->d_name.name, "preserve"))
+               return -EINVAL;
+
+       parent = dentry->d_parent;
+       if (!parent || sscanf(parent->d_name.name, "cpu%u", &cpu) != 1)
+               return -EINVAL;
+
+       if (cpu >= nr_cpu_ids || !cpu_possible(cpu) ||
+           !cpu_is_hotpluggable(cpu)) {
+               return -EINVAL;
+       }
+
+       *cpup = cpu;
+       return 0;
+}
+
+static bool cpu_preserve_can_preserve(struct liveupdate_file_handler *handler,
+                                     struct file *file)
+{
+       unsigned int cpu;
+
+       return IS_ENABLED(CONFIG_LIVEUPDATE_ONCORE) && file_to_cpu(file, &cpu) 
== 0;
+}
+
+static int cpu_preserve_preserve(struct liveupdate_file_op_args *args)
+{
+       struct cpu_preserved_file_ser *fser;
+       unsigned int cpu;
+       int ret;
+
+       ret = file_to_cpu(args->file, &cpu);
+       if (ret)
+               return ret;
+
+       ret = cpu_preserve(cpu);
+       if (ret)
+               return ret;
+
+       fser = kho_alloc_preserve(sizeof(*fser));
+       if (IS_ERR(fser)) {
+               cpu_unpreserve(cpu);
+               return PTR_ERR(fser);
+       }
+
+       memset(fser, 0, sizeof(*fser));
+       fser->cpu = cpu;
+
+       scoped_guard(mutex, &cpu_preserved_lock)
+               fser->stack_pa = cpu_preserved_outgoing.pcpus[cpu].stack_pa;
+
+       args->serialized_data = virt_to_phys(fser);
+       return 0;
+}
+
+static void cpu_preserve_unpreserve(struct liveupdate_file_op_args *args)
+{
+       struct cpu_preserved_file_ser *fser;
+       unsigned int cpu;
+
+       if (!args->serialized_data)
+               return;
+
+       fser = phys_to_virt(args->serialized_data);
+       cpu = fser->cpu;
+
+       cpu_unpreserve(cpu);
+
+       kho_unpreserve_free(fser);
+}
+
+static void cpu_preserve_restore_incoming_cpu(struct liveupdate_session 
*session,
+                                             struct cpu_preserved_file_ser 
*fser)
+{
+       unsigned int cpu = fser->cpu;
+
+       scoped_guard(mutex, &cpu_preserved_lock) {
+               cpumask_set_cpu(cpu, &cpu_preserved_incoming.mask);
+               cpumask_set_cpu(cpu, &cpu_preserved_mask);
+
+               if (!cpu_preserved_incoming.pcpus) {
+                       cpu_preserved_incoming.pcpus =
+                               kcalloc(nr_cpu_ids,
+                                       sizeof(*cpu_preserved_incoming.pcpus),
+                                       GFP_KERNEL);
+                       WRITE_ONCE(cpu_preserved_host_pcpus_va,
+                                  cpu_preserved_incoming.pcpus);
+                       cpu_preserved_clean(&cpu_preserved_host_pcpus_va);
+               }
+
+               if (cpu_preserved_incoming.pcpus)
+                       cpu_preserved_incoming.pcpus[cpu].stack_pa = 
fser->stack_pa;
+
+               cpu_preserved_clean(&cpu_preserved_mask);
+       }
+}
+
+static int cpu_preserve_retrieve(struct liveupdate_file_op_args *args)
+{
+       struct cpu_preserved_file_ser *fser;
+       struct file *file;
+       char path[64];
+
+       if (!args->serialized_data)
+               return -EINVAL;
+
+       fser = phys_to_virt(args->serialized_data);
+
+       snprintf(path, sizeof(path),
+                "/sys/devices/system/cpu/cpu%u/preserve", fser->cpu);
+       file = filp_open(path, O_RDONLY, 0);
+       if (IS_ERR(file))
+               return PTR_ERR(file);
+
+       args->file = file;
+       cpu_preserve_restore_incoming_cpu(args->session, fser);
+       cpu_preserved_detach_workload(fser->cpu);
+
+       return 0;
+}
+
+static void cpu_preserve_finish(struct liveupdate_file_op_args *args)
+{
+       struct cpu_preserved_file_ser *fser;
+
+       if (!args->serialized_data)
+               return;
+
+       fser = phys_to_virt(args->serialized_data);
+       if (args->retrieve_status <= 0)
+               cpu_preserve_restore_incoming_cpu(args->session, fser);
+
+       cpu_unpreserve(fser->cpu);
+
+       kho_restore_free(fser);
+}
+
+static const struct liveupdate_file_ops cpu_preserve_file_ops = {
+       .can_preserve = cpu_preserve_can_preserve,
+       .preserve     = cpu_preserve_preserve,
+       .retrieve     = cpu_preserve_retrieve,
+       .unpreserve   = cpu_preserve_unpreserve,
+       .finish       = cpu_preserve_finish,
+       .owner        = THIS_MODULE,
+};
+
+static struct liveupdate_file_handler cpu_preserve_handler = {
+       .ops        = &cpu_preserve_file_ops,
+       .compatible = CPU_PRESERVED_LUO_FH_COMPATIBLE,
+};
+
 static int cpu_preserve_reboot_notify(struct notifier_block *nb,
                                      unsigned long action, void *data)
 {
@@ -1555,6 +1721,21 @@ static int __init cpu_preserve_early_init(void)
        cpu_preserved_incoming.pcpus_ser = NULL;
        cpu_preserved_global_ser = NULL;
 
+       err = liveupdate_register_file_handler(&cpu_preserve_handler);
+       if (err && err != -EOPNOTSUPP) {
+               pr_err("Could not register cpu_preserve file handler: %pe\n",
+                      ERR_PTR(err));
+               return err;
+       }
+
+       err = liveupdate_register_flb(&cpu_preserve_handler,
+                                     &cpu_preserved_flb);
+       if (err && err != -EOPNOTSUPP) {
+               pr_err("Could not register cpu_preserved FLB: %pe\n",
+                      ERR_PTR(err));
+               return err;
+       }
+
        /* Retrieve incoming preserved CPUs before secondary CPU bringup */
        if (liveupdate_enabled())
                liveupdate_flb_get_incoming(&cpu_preserved_flb, &obj);
-- 
2.55.0.1082.g2b9226bbc0-goog


Reply via email to