powerpc/pseries: Add pseries hotplug workqueue

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>
This commit is contained in:
John Allen 2016-07-07 10:00:34 -05:00 committed by Michael Ellerman
parent 8fbaa51d43
commit 9054619ef5

View file

@ -27,6 +27,15 @@
#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);