[PATCH 2/3] ocxl: Add pseries-specific code

christophe lombard clombard at linux.vnet.ibm.com
Tue Oct 29 01:40:59 AEDT 2019


On 22/10/2019 09:52, christophe lombard wrote:
> pseries.c implements the guest-specific callbacks for the backend API.
> 
> The hypervisor calls provide an interface to configure and interact with
> OpenCAPI devices. It matches the last version of the 'PAPR changes'
> document.
> 
> The following hcalls are supported:
> H_OCXL_CONFIG_ADAPTER   Used to configure OpenCAPI adapter characteristics.
> 
> H_OCXL_CONFIG_SPA       Used to configure the schedule process area (SPA)
>                          table for an OCAPI device.
> 
> H_OCXL_GET_FAULT_STATE  Used to retrieve fault information from an
>                          OpenCAPI device.
> 
> H_OCXL_HANDLE_FAULT     Used to respond to an OpenCAPI fault.
> 
> Each previous hcall supports a config flag parameter, to allows the guest
> to manage the CAPI device.
> 
> The current values 0xf004 to 0xf007 have been chosen according the
> available QEMU hcall values which are specific to qemu / KVM-on-POWER.
> 
> Two parameters are common to all hcalls (buid and config_addr) that will
> be used to allow QEMU to recover the PCI device.
> 
> Signed-off-by: Christophe Lombard <clombard at linux.vnet.ibm.com>
> ---
>   drivers/misc/ocxl/Makefile        |   1 +
>   drivers/misc/ocxl/main.c          |   4 +
>   drivers/misc/ocxl/ocxl_internal.h |   1 +
>   drivers/misc/ocxl/pseries.c       | 450 ++++++++++++++++++++++++++++++
>   4 files changed, 456 insertions(+)
>   create mode 100644 drivers/misc/ocxl/pseries.c
> 
> diff --git a/drivers/misc/ocxl/Makefile b/drivers/misc/ocxl/Makefile
> index bfdaeb232b83..3474e912c402 100644
> --- a/drivers/misc/ocxl/Makefile
> +++ b/drivers/misc/ocxl/Makefile
> @@ -5,6 +5,7 @@ ocxl-y				+= main.o pci.o config.o file.o pasid.o mmio.o
>   ocxl-y				+= link.o context.o afu_irq.o sysfs.o trace.o
>   ocxl-y				+= core.o
>   ocxl-$(CONFIG_PPC_POWERNV)	+= powernv.o
> +ocxl-$(CONFIG_PPC_PSERIES)	+= pseries.o
> 
>   obj-$(CONFIG_OCXL)		+= ocxl.o
> 
> diff --git a/drivers/misc/ocxl/main.c b/drivers/misc/ocxl/main.c
> index 95df2ba4d473..bdd9ffa7f769 100644
> --- a/drivers/misc/ocxl/main.c
> +++ b/drivers/misc/ocxl/main.c
> @@ -16,6 +16,10 @@ static int __init init_ocxl(void)
> 
>   	if (cpu_has_feature(CPU_FTR_HVMODE))
>   		ocxl_ops = &ocxl_powernv_ops;
> +#ifdef CONFIG_PPC_PSERIES
> +	else
> +		ocxl_ops = &ocxl_pseries_ops;
> +#endif
> 
>   	rc = pci_register_driver(&ocxl_pci_driver);
>   	if (rc) {
> diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
> index 2bdea279bdc6..c18b32df3fe5 100644
> --- a/drivers/misc/ocxl/ocxl_internal.h
> +++ b/drivers/misc/ocxl/ocxl_internal.h
> @@ -104,6 +104,7 @@ struct ocxl_backend_ops {
>   };
> 
>   extern const struct ocxl_backend_ops ocxl_powernv_ops;
> +extern const struct ocxl_backend_ops ocxl_pseries_ops;
>   extern const struct ocxl_backend_ops *ocxl_ops;
> 
>   int ocxl_create_cdev(struct ocxl_afu *afu);
> diff --git a/drivers/misc/ocxl/pseries.c b/drivers/misc/ocxl/pseries.c
> new file mode 100644
> index 000000000000..1d4942d713f7
> --- /dev/null
> +++ b/drivers/misc/ocxl/pseries.c
> @@ -0,0 +1,450 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2018 IBM Corp.
> +#include <misc/ocxl-config.h>
> +#include "ocxl_internal.h"
> +#include <asm/xive.h>
> +
> +#define H_OCXL_CONFIG_ADAPTER	0xf004
> +#define H_OCXL_CONFIG_SPA	0xf005
> +#define H_OCXL_GET_FAULT_STATE	0xf006
> +#define H_OCXL_HANDLE_FAULT	0xf007
> +
> +#define H_CONFIG_ADAPTER_SETUP		1
> +#define H_CONFIG_ADAPTER_RELEASE	2
> +#define H_CONFIG_ADAPTER_GET_ACTAG	3
> +#define H_CONFIG_ADAPTER_GET_PASID	4
> +#define H_CONFIG_ADAPTER_SET_TL		5
> +#define H_CONFIG_ADAPTER_ALLOC_IRQ	6
> +#define H_CONFIG_ADAPTER_FREE_IRQ	7
> +
> +#define H_CONFIG_SPA_SET		1
> +#define H_CONFIG_SPA_UPDATE		2
> +#define H_CONFIG_SPA_REMOVE		3
> +
> +static char *config_adapter_names[] = {
> +	"UNKNOWN_OP",		/* 0 undefined */
> +	"SETUP",		/* 1 */
> +	"RELEASE",		/* 2 */
> +	"GET_ACTAG",		/* 3 */
> +	"GET_PASID",		/* 4 */
> +	"SET_TL",		/* 5 */
> +	"ALLOC_IRQ",		/* 6 */
> +	"FREE_IRQ",		/* 7 */
> +};
> +
> +static char *config_spa_names[] = {
> +	"UNKNOWN_OP",		/* 0 undefined */
> +	"SET",			/* 1 */
> +	"UPDATE",		/* 2 */
> +	"REMOVE",		/* 3 */
> +};
> +
> +static char *op_str(unsigned int op, char *names[], int len)
> +{
> +	if (op >= len)
> +		return "UNKNOWN_OP";
> +	return names[op];
> +}
> +
> +#define OP_STR(op, names)	op_str(op, names, ARRAY_SIZE(names))
> +#define OP_STR_CA(op)		OP_STR(op, config_adapter_names)
> +#define OP_STR_CS(op)		OP_STR(op, config_spa_names)
> +
> +#define _PRINT_MSG(rc, format, ...)					\
> +	{								\
> +		if (rc != H_SUCCESS && rc != H_CONTINUE)		\
> +			pr_err(format, __VA_ARGS__);			\
> +		else							\
> +			pr_debug(format, __VA_ARGS__);			\
> +	}								\
> +
> +struct pseries_data {
> +	u64 buid;
> +	u32 config_addr;
> +	int fault_lisn;
> +	int host_irq;
> +};
> +
> +static unsigned int busy_delay(long rc)
> +{
> +	unsigned int ms = 0;
> +
> +	if (H_IS_LONG_BUSY(rc))
> +		ms = get_longbusy_msecs(rc);
> +	else if (rc == H_BUSY)
> +		ms = 10;
> +
> +	if (ms)
> +		mdelay(ms);
> +
> +	return ms;
> +}
> +
> +static long config_adapter(unsigned long *retbuf, u64 *params)
> +{
> +	long rc;
> +
> +	do {
> +		rc = plpar_hcall9(H_OCXL_CONFIG_ADAPTER, retbuf,
> +				  params[0], params[1], params[2],
> +				  params[3], params[4]);
> +	} while (busy_delay(rc));
> +
> +	_PRINT_MSG(rc, "%s - buid:%#llx, config_addr: %#llx "
> +			"%s(%#llx, %#llx, "
> +			"retbuf1: %#lx, retbuf2: %#lx, retbuf3: %#lx): %li\n",
> +			__func__, params[0], params[1],
> +			OP_STR_CA(params[2]),
> +			params[3], params[4],
> +			retbuf[0], retbuf[1], retbuf[2], rc);
> +
> +	switch (rc) {
> +	case H_SUCCESS:
> +		return 0;
> +	case H_PARAMETER:
> +	case H_FUNCTION:
> +	case H_NOT_FOUND:
> +	case H_NOT_AVAILABLE:
> +	case H_SG_LIST:
> +		return -EINVAL;
> +	case H_AUTHORITY:
> +	case H_RESOURCE:
> +	case H_HARDWARE:
> +	case H_STATE:
> +	case H_BUSY:
> +		return -EBUSY;
> +	default:
> +		WARN(1, "Unexpected return code: %lx", rc);
> +		return -EINVAL;
> +	}
> +}
> +
> +static int config_spa(unsigned long *retbuf, u64 *params)
> +{
> +	long rc;
> +
> +	do {
> +		rc = plpar_hcall9(H_OCXL_CONFIG_SPA, retbuf,
> +				  params[0], params[1], params[2],
> +				  params[3], params[4], params[5],
> +				  params[6]);
> +	} while (busy_delay(rc));
> +
> +	_PRINT_MSG(rc, "%s - buid:%#llx, config_addr: %#llx "
> +			"%s(%#llx, %#llx, %#llx, %#llx, "
> +			"retbuf1: %#lx, retbuf2: %#lx, retbuf3: %#lx): %li\n",
> +			__func__, params[0], params[1],
> +			OP_STR_CS(params[2]),
> +			params[3], params[4], params[5], params[6],
> +			retbuf[0], retbuf[1], retbuf[2], rc);
> +
> +	switch (rc) {
> +	case H_SUCCESS:
> +		return 0;
> +	case H_PARAMETER:
> +	case H_FUNCTION:
> +		return -EINVAL;
> +	case H_AUTHORITY:
> +	case H_RESOURCE:
> +	case H_HARDWARE:
> +	case H_STATE:
> +	case H_BUSY:
> +		return -EBUSY;
> +	default:
> +		WARN(1, "Unexpected return code: %lx", rc);
> +		return -EINVAL;
> +	}
> +}
> +
> +static u64 get_buid(struct pci_dev *dev)
> +{
> +	struct device_node *dn;
> +	struct pci_dn *pdn;
> +
> +	dn = pci_device_to_OF_node(dev);
> +	pdn = PCI_DN(dn);
> +	return pdn->phb->buid;
> +}
> +
> +static u32 get_config_addr(struct pci_dev *dev)
> +{
> +	int bus, devfn;
> +	u32 config_addr;
> +
> +	bus = dev->bus->number;
> +	devfn = dev->devfn;
> +	config_addr = ((bus & 0xFF) << 16) + ((devfn & 0xFF) << 8);
> +	return config_addr;
> +}
> +
> +static void ack_irq(void *data, u64 tfc)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	long rc;
> +
> +	do {
> +		rc = plpar_hcall(H_OCXL_HANDLE_FAULT, retbuf,
> +				 pdata->buid, pdata->config_addr, tfc);
> +	} while (busy_delay(rc));
> +
> +	_PRINT_MSG(rc, "%s - buid:%#llx, config_addr: %#x "
> +			"(tfc: %#llx): %li\n",
> +			__func__, pdata->buid, pdata->config_addr,
> +			tfc, rc);
> +}
> +
> +static int alloc_xive_irq(void *data, u32 *irq,
> +			  u64 *trigger_addr)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +	struct xive_irq_data *xd;
> +	int virq;
> +	long rc;
> +
> +	params[0] = pdata->buid;
> +	params[1] = pdata->config_addr;
> +	params[2] = H_CONFIG_ADAPTER_ALLOC_IRQ;
> +
> +	rc = config_adapter(retbuf, params);
> +	if (rc)
> +		return rc;
> +
> +	*irq = retbuf[0];
> +
> +	virq = irq_create_mapping(NULL, *irq);
> +	if (!virq) {
> +		pr_err("irq_create_mapping failed\n");
> +		return -EINVAL;
> +	}
> +
> +	xd = irq_get_handler_data(virq);
> +	if (!xd) {
> +		pr_err("irq_get_handler_data failed\n");

irq_dispose_mapping(virq) has to be called in case of error.

> +		return -EINVAL;
> +	}
> +	/**trigger_addr = xd->trig_page;*/
> +	*trigger_addr = xd->eoi_page;
> +
> +	pr_debug("%s - buid: %#llx, irq: %d, trigger_addr: %#llx\n",
> +		 __func__, pdata->buid, *irq, *trigger_addr);

Same remark, here for irq_dispose_mapping(virq).
irq_create_mapping() was called previously to get trigger_addr,
so no reason to keep virq outside this function.

> +	return rc;
> +}
> +
> +static void free_xive_irq(void *data, u32 irq)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +
> +	params[0] = pdata->buid;
> +	params[1] = pdata->config_addr;
> +	params[2] = H_CONFIG_ADAPTER_FREE_IRQ;
> +	params[3] = irq;
> +
> +	config_adapter(retbuf, params);
> +}
> +
> +static int get_actag(struct pci_dev *dev, u16 *base,
> +		     u16 *enabled, u16 *supported)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +	int rc = 0;
> +
> +	params[0] = get_buid(dev);
> +	params[1] = get_config_addr(dev);
> +	params[2] = H_CONFIG_ADAPTER_GET_ACTAG;
> +
> +	rc = config_adapter(retbuf, params);
> +	if (!rc) {
> +		*base = retbuf[0];
> +		*enabled = retbuf[1];
> +		*supported = retbuf[2];
> +	}
> +	return rc;
> +}
> +
> +static void get_fault_state(void *data, u64 *dsisr, u64 *dar,
> +			    u64 *pe_handle, int *pid)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	long rc;
> +
> +	do {
> +		rc = plpar_hcall9(H_OCXL_GET_FAULT_STATE, retbuf,
> +				  pdata->buid, pdata->config_addr);
> +	} while (busy_delay(rc));
> +
> +	_PRINT_MSG(rc, "%s - buid:%#llx, config_addr: %#x "
> +			"(out1: %#lx, out2: %#lx, out3: %#lx, "
> +			"out4: %#lx): %li\n",
> +			__func__, pdata->buid, pdata->config_addr,
> +			retbuf[0], retbuf[1], retbuf[2], retbuf[3], rc);
> +
> +	*dsisr = retbuf[0];
> +	*dar = retbuf[1];
> +	*pe_handle = retbuf[2];
> +	*pid = retbuf[3];
> +}
> +
> +static int get_pasid_count(struct pci_dev *dev, int *count)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +	int rc = 0;
> +
> +	params[0] = get_buid(dev);
> +	params[1] = get_config_addr(dev);
> +	params[2] = H_CONFIG_ADAPTER_GET_PASID;
> +
> +	rc = config_adapter(retbuf, params);
> +	if (!rc)
> +		*count = retbuf[0];
> +	return rc;
> +}
> +
> +static void platform_release(void *data)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +
> +	params[0] = pdata->buid;
> +	params[1] = pdata->config_addr;
> +	params[2] = H_CONFIG_ADAPTER_RELEASE;
> +	params[3] = pdata->host_irq;
> +	params[4] = pdata->fault_lisn;
> +
> +	config_adapter(retbuf, params);
> +
> +	kfree(pdata);
> +}
> +
> +static int platform_setup(struct pci_dev *dev, int PE_mask, int *hwirq,
> +			  void **data)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata;
> +	int rc = 0;
> +
> +	rc = of_property_read_u32(dev->dev.of_node, "fault-lisn", hwirq);
> +	if (rc) {
> +		pr_err("fault-lisn not found (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata)
> +		return -ENOMEM;
> +
> +	pdata->buid = get_buid(dev);
> +	pdata->config_addr = get_config_addr(dev);
> +	*data = pdata;
> +
> +	params[0] = pdata->buid;
> +	params[1] = pdata->config_addr;
> +	params[2] = H_CONFIG_ADAPTER_SETUP;
> +	params[3] = PE_mask;
> +	params[4] = *hwirq;
> +
> +	rc = config_adapter(retbuf, params);
> +	if (!rc) {
> +		pdata->fault_lisn = *hwirq;
> +		pdata->host_irq = retbuf[0];
> +	}
> +	return rc;
> +}
> +
> +static int remove_pe(void *data, int pasid, u32 *pid,
> +		     u32 *tid, int *pe_handle)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +	int rc;
> +
> +	params[0] = pdata->buid;
> +	params[1] = pdata->config_addr;
> +	params[2] = H_CONFIG_SPA_REMOVE;
> +	params[3] = pasid;
> +
> +	rc = config_spa(retbuf, params);
> +	if (!rc) {
> +		*pid = retbuf[0];
> +		*tid = retbuf[1];
> +		*pe_handle = retbuf[2];
> +	}
> +	return rc;
> +}
> +
> +static int set_pe(void *data, int pasid, u32 pidr, u32 tidr,
> +		  u64 amr, int *pe_handle)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +	int rc;
> +
> +	params[0] = pdata->buid;
> +	params[1] = pdata->config_addr;
> +	params[2] = H_CONFIG_SPA_SET;
> +	params[3] = pasid;
> +	params[4] = pidr;
> +	params[5] = tidr;
> +	params[6] = amr;
> +
> +	rc = config_spa(retbuf, params);
> +	if (!rc)
> +		*pe_handle = retbuf[0];
> +
> +	return rc;
> +}
> +
> +static int set_tl(struct pci_dev *dev, int tl_dvsec)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +
> +	params[0] = get_buid(dev);
> +	params[1] = get_config_addr(dev);
> +	params[2] = H_CONFIG_ADAPTER_SET_TL;
> +	params[3] = tl_dvsec;
> +
> +	return config_adapter(retbuf, params);
> +}
> +
> +static int update_pe(void *data, int pasid, __u16 tid)
> +{
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
> +	struct pseries_data *pdata = data;
> +	u64 params[PLPAR_HCALL9_BUFSIZE];
> +
> +	params[0] = pdata->buid;
> +	params[1] = pdata->config_addr;
> +	params[2] = H_CONFIG_SPA_UPDATE;
> +	params[3] = pasid;
> +	params[4] = tid;
> +
> +	return config_spa(retbuf, params);
> +}
> +
> +const struct ocxl_backend_ops ocxl_pseries_ops = {
> +	.module = THIS_MODULE,
> +	.ack_irq = ack_irq,
> +	.alloc_xive_irq = alloc_xive_irq,
> +	.free_xive_irq = free_xive_irq,
> +	.get_actag = get_actag,
> +	.get_fault_state = get_fault_state,
> +	.get_pasid_count = get_pasid_count,
> +	.platform_release = platform_release,
> +	.platform_setup = platform_setup,
> +	.remove_pe = remove_pe,
> +	.set_pe = set_pe,
> +	.set_tl = set_tl,
> +	.update_pe = update_pe,
> +};
> 



More information about the Linuxppc-dev mailing list