[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