powerpc/pseries: Add pseries hotplug workqueue
authorJohn Allen <jallen@linux.vnet.ibm.com>
Thu, 7 Jul 2016 15:00:34 +0000 (10:00 -0500)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 19 Jul 2016 10:12:30 +0000 (20:12 +1000)
In support of PAPR changes to add a new hotplug interrupt, introduce a
hotplug workqueue to avoid processing hotplug events in interrupt context.
We will also take advantage of the queue on PowerVM to ensure hotplug
events initiated from different sources (HMC and PRRN events) are handled
and serialized properly.

Signed-off-by: John Allen <jallen@linux.vnet.ibm.com>
Reviewed-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/platforms/pseries/dlpar.c

index 2b93ae8..66a77d7 100644 (file)
 #include <asm/uaccess.h>
 #include <asm/rtas.h>
 
+struct workqueue_struct *pseries_hp_wq;
+
+struct pseries_hp_work {
+       struct work_struct work;
+       struct pseries_hp_errorlog *errlog;
+       struct completion *hp_completion;
+       int *rc;
+};
+
 struct cc_workarea {
        __be32  drc_index;
        __be32  zero;
@@ -368,10 +377,51 @@ static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
        return rc;
 }
 
+void pseries_hp_work_fn(struct work_struct *work)
+{
+       struct pseries_hp_work *hp_work =
+                       container_of(work, struct pseries_hp_work, work);
+
+       if (hp_work->rc)
+               *(hp_work->rc) = handle_dlpar_errorlog(hp_work->errlog);
+       else
+               handle_dlpar_errorlog(hp_work->errlog);
+
+       if (hp_work->hp_completion)
+               complete(hp_work->hp_completion);
+
+       kfree(hp_work->errlog);
+       kfree((void *)work);
+}
+
+void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog,
+                        struct completion *hotplug_done, int *rc)
+{
+       struct pseries_hp_work *work;
+       struct pseries_hp_errorlog *hp_errlog_copy;
+
+       hp_errlog_copy = kmalloc(sizeof(struct pseries_hp_errorlog),
+                                GFP_KERNEL);
+       memcpy(hp_errlog_copy, hp_errlog, sizeof(struct pseries_hp_errorlog));
+
+       work = kmalloc(sizeof(struct pseries_hp_work), GFP_KERNEL);
+       if (work) {
+               INIT_WORK((struct work_struct *)work, pseries_hp_work_fn);
+               work->errlog = hp_errlog_copy;
+               work->hp_completion = hotplug_done;
+               work->rc = rc;
+               queue_work(pseries_hp_wq, (struct work_struct *)work);
+       } else {
+               *rc = -ENOMEM;
+               complete(hotplug_done);
+       }
+}
+
 static ssize_t dlpar_store(struct class *class, struct class_attribute *attr,
                           const char *buf, size_t count)
 {
        struct pseries_hp_errorlog *hp_elog;
+       struct completion hotplug_done;
        const char *arg;
        int rc;
 
@@ -450,6 +500,8 @@ static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store);
 
 static int __init pseries_dlpar_init(void)
 {
+       pseries_hp_wq = alloc_workqueue("pseries hotplug workqueue",
+                                       WQ_UNBOUND, 1);
        return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
 }
 machine_device_initcall(pseries, pseries_dlpar_init);