[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <4727605e-d53f-db26-83de-c86761add566@linux.ibm.com>
Date: Wed, 17 Apr 2019 15:22:22 +0200
From: Frederic Barrat <fbarrat@...ux.ibm.com>
To: "Alastair D'Silva" <alastair@....ibm.com>, alastair@...ilva.org
Cc: Andrew Donnellan <andrew.donnellan@....ibm.com>,
Arnd Bergmann <arnd@...db.de>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
linux-kernel@...r.kernel.org, linuxppc-dev@...ts.ozlabs.org
Subject: Re: [PATCH v4 1/7] ocxl: Split pci.c
Le 27/03/2019 à 06:31, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@...ilva.org>
>
> In preparation for making core code available for external drivers,
> move the core code out of pci.c and into core.c
>
> Signed-off-by: Alastai
Acked-by: Frederic Barrat <fbarrat@...ux.ibm.com>
> drivers/misc/ocxl/Makefile | 1 +
> drivers/misc/ocxl/core.c | 517 +++++++++++++++++++++++++++++
> drivers/misc/ocxl/ocxl_internal.h | 5 +
> drivers/misc/ocxl/pci.c | 519 +-----------------------------
> 4 files changed, 524 insertions(+), 518 deletions(-)
> create mode 100644 drivers/misc/ocxl/core.c
>
> diff --git a/drivers/misc/ocxl/Makefile b/drivers/misc/ocxl/Makefile
> index 5229dcda8297..bc4e39bfda7b 100644
> --- a/drivers/misc/ocxl/Makefile
> +++ b/drivers/misc/ocxl/Makefile
> @@ -3,6 +3,7 @@ ccflags-$(CONFIG_PPC_WERROR) += -Werror
>
> ocxl-y += main.o pci.o config.o file.o pasid.o
> ocxl-y += link.o context.o afu_irq.o sysfs.o trace.o
> +ocxl-y += core.o
> obj-$(CONFIG_OCXL) += ocxl.o
>
> # For tracepoints to include our trace.h from tracepoint infrastructure:
> diff --git a/drivers/misc/ocxl/core.c b/drivers/misc/ocxl/core.c
> new file mode 100644
> index 000000000000..1a4411b72d35
> --- /dev/null
> +++ b/drivers/misc/ocxl/core.c
> @@ -0,0 +1,517 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2019 IBM Corp.
> +#include <linux/idr.h>
> +#include "ocxl_internal.h"
> +
> +static struct ocxl_fn *ocxl_fn_get(struct ocxl_fn *fn)
> +{
> + return (get_device(&fn->dev) == NULL) ? NULL : fn;
> +}
> +
> +static void ocxl_fn_put(struct ocxl_fn *fn)
> +{
> + put_device(&fn->dev);
> +}
> +
> +struct ocxl_afu *ocxl_afu_get(struct ocxl_afu *afu)
> +{
> + return (get_device(&afu->dev) == NULL) ? NULL : afu;
> +}
> +
> +void ocxl_afu_put(struct ocxl_afu *afu)
> +{
> + put_device(&afu->dev);
> +}
> +
> +static struct ocxl_afu *alloc_afu(struct ocxl_fn *fn)
> +{
> + struct ocxl_afu *afu;
> +
> + afu = kzalloc(sizeof(struct ocxl_afu), GFP_KERNEL);
> + if (!afu)
> + return NULL;
> +
> + mutex_init(&afu->contexts_lock);
> + mutex_init(&afu->afu_control_lock);
> + idr_init(&afu->contexts_idr);
> + afu->fn = fn;
> + ocxl_fn_get(fn);
> + return afu;
> +}
> +
> +static void free_afu(struct ocxl_afu *afu)
> +{
> + idr_destroy(&afu->contexts_idr);
> + ocxl_fn_put(afu->fn);
> + kfree(afu);
> +}
> +
> +static void free_afu_dev(struct device *dev)
> +{
> + struct ocxl_afu *afu = to_ocxl_afu(dev);
> +
> + ocxl_unregister_afu(afu);
> + free_afu(afu);
> +}
> +
> +static int set_afu_device(struct ocxl_afu *afu, const char *location)
> +{
> + struct ocxl_fn *fn = afu->fn;
> + int rc;
> +
> + afu->dev.parent = &fn->dev;
> + afu->dev.release = free_afu_dev;
> + rc = dev_set_name(&afu->dev, "%s.%s.%hhu", afu->config.name, location,
> + afu->config.idx);
> + return rc;
> +}
> +
> +static int assign_afu_actag(struct ocxl_afu *afu, struct pci_dev *dev)
> +{
> + struct ocxl_fn *fn = afu->fn;
> + int actag_count, actag_offset;
> +
> + /*
> + * if there were not enough actags for the function, each afu
> + * reduces its count as well
> + */
> + actag_count = afu->config.actag_supported *
> + fn->actag_enabled / fn->actag_supported;
> + actag_offset = ocxl_actag_afu_alloc(fn, actag_count);
> + if (actag_offset < 0) {
> + dev_err(&afu->dev, "Can't allocate %d actags for AFU: %d\n",
> + actag_count, actag_offset);
> + return actag_offset;
> + }
> + afu->actag_base = fn->actag_base + actag_offset;
> + afu->actag_enabled = actag_count;
> +
> + ocxl_config_set_afu_actag(dev, afu->config.dvsec_afu_control_pos,
> + afu->actag_base, afu->actag_enabled);
> + dev_dbg(&afu->dev, "actag base=%d enabled=%d\n",
> + afu->actag_base, afu->actag_enabled);
> + return 0;
> +}
> +
> +static void reclaim_afu_actag(struct ocxl_afu *afu)
> +{
> + struct ocxl_fn *fn = afu->fn;
> + int start_offset, size;
> +
> + start_offset = afu->actag_base - fn->actag_base;
> + size = afu->actag_enabled;
> + ocxl_actag_afu_free(afu->fn, start_offset, size);
> +}
> +
> +static int assign_afu_pasid(struct ocxl_afu *afu, struct pci_dev *dev)
> +{
> + struct ocxl_fn *fn = afu->fn;
> + int pasid_count, pasid_offset;
> +
> + /*
> + * We only support the case where the function configuration
> + * requested enough PASIDs to cover all AFUs.
> + */
> + pasid_count = 1 << afu->config.pasid_supported_log;
> + pasid_offset = ocxl_pasid_afu_alloc(fn, pasid_count);
> + if (pasid_offset < 0) {
> + dev_err(&afu->dev, "Can't allocate %d PASIDs for AFU: %d\n",
> + pasid_count, pasid_offset);
> + return pasid_offset;
> + }
> + afu->pasid_base = fn->pasid_base + pasid_offset;
> + afu->pasid_count = 0;
> + afu->pasid_max = pasid_count;
> +
> + ocxl_config_set_afu_pasid(dev, afu->config.dvsec_afu_control_pos,
> + afu->pasid_base,
> + afu->config.pasid_supported_log);
> + dev_dbg(&afu->dev, "PASID base=%d, enabled=%d\n",
> + afu->pasid_base, pasid_count);
> + return 0;
> +}
> +
> +static void reclaim_afu_pasid(struct ocxl_afu *afu)
> +{
> + struct ocxl_fn *fn = afu->fn;
> + int start_offset, size;
> +
> + start_offset = afu->pasid_base - fn->pasid_base;
> + size = 1 << afu->config.pasid_supported_log;
> + ocxl_pasid_afu_free(afu->fn, start_offset, size);
> +}
> +
> +static int reserve_fn_bar(struct ocxl_fn *fn, int bar)
> +{
> + struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> + int rc, idx;
> +
> + if (bar != 0 && bar != 2 && bar != 4)
> + return -EINVAL;
> +
> + idx = bar >> 1;
> + if (fn->bar_used[idx]++ == 0) {
> + rc = pci_request_region(dev, bar, "ocxl");
> + if (rc)
> + return rc;
> + }
> + return 0;
> +}
> +
> +static void release_fn_bar(struct ocxl_fn *fn, int bar)
> +{
> + struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> + int idx;
> +
> + if (bar != 0 && bar != 2 && bar != 4)
> + return;
> +
> + idx = bar >> 1;
> + if (--fn->bar_used[idx] == 0)
> + pci_release_region(dev, bar);
> + WARN_ON(fn->bar_used[idx] < 0);
> +}
> +
> +static int map_mmio_areas(struct ocxl_afu *afu, struct pci_dev *dev)
> +{
> + int rc;
> +
> + rc = reserve_fn_bar(afu->fn, afu->config.global_mmio_bar);
> + if (rc)
> + return rc;
> +
> + rc = reserve_fn_bar(afu->fn, afu->config.pp_mmio_bar);
> + if (rc) {
> + release_fn_bar(afu->fn, afu->config.global_mmio_bar);
> + return rc;
> + }
> +
> + afu->global_mmio_start =
> + pci_resource_start(dev, afu->config.global_mmio_bar) +
> + afu->config.global_mmio_offset;
> + afu->pp_mmio_start =
> + pci_resource_start(dev, afu->config.pp_mmio_bar) +
> + afu->config.pp_mmio_offset;
> +
> + afu->global_mmio_ptr = ioremap(afu->global_mmio_start,
> + afu->config.global_mmio_size);
> + if (!afu->global_mmio_ptr) {
> + release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
> + release_fn_bar(afu->fn, afu->config.global_mmio_bar);
> + dev_err(&dev->dev, "Error mapping global mmio area\n");
> + return -ENOMEM;
> + }
> +
> + /*
> + * Leave an empty page between the per-process mmio area and
> + * the AFU interrupt mappings
> + */
> + afu->irq_base_offset = afu->config.pp_mmio_stride + PAGE_SIZE;
> + return 0;
> +}
> +
> +static void unmap_mmio_areas(struct ocxl_afu *afu)
> +{
> + if (afu->global_mmio_ptr) {
> + iounmap(afu->global_mmio_ptr);
> + afu->global_mmio_ptr = NULL;
> + }
> + afu->global_mmio_start = 0;
> + afu->pp_mmio_start = 0;
> + release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
> + release_fn_bar(afu->fn, afu->config.global_mmio_bar);
> +}
> +
> +static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
> +{
> + int rc;
> +
> + rc = ocxl_config_read_afu(dev, &afu->fn->config, &afu->config, afu_idx);
> + if (rc)
> + return rc;
> +
> + rc = set_afu_device(afu, dev_name(&dev->dev));
> + if (rc)
> + return rc;
> +
> + rc = assign_afu_actag(afu, dev);
> + if (rc)
> + return rc;
> +
> + rc = assign_afu_pasid(afu, dev);
> + if (rc) {
> + reclaim_afu_actag(afu);
> + return rc;
> + }
> +
> + rc = map_mmio_areas(afu, dev);
> + if (rc) {
> + reclaim_afu_pasid(afu);
> + reclaim_afu_actag(afu);
> + return rc;
> + }
> + return 0;
> +}
> +
> +static void deconfigure_afu(struct ocxl_afu *afu)
> +{
> + unmap_mmio_areas(afu);
> + reclaim_afu_pasid(afu);
> + reclaim_afu_actag(afu);
> +}
> +
> +static int activate_afu(struct pci_dev *dev, struct ocxl_afu *afu)
> +{
> + int rc;
> +
> + ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 1);
> + /*
> + * Char device creation is the last step, as processes can
> + * call our driver immediately, so all our inits must be finished.
> + */
> + rc = ocxl_create_cdev(afu);
> + if (rc)
> + return rc;
> + return 0;
> +}
> +
> +static void deactivate_afu(struct ocxl_afu *afu)
> +{
> + struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
> +
> + ocxl_destroy_cdev(afu);
> + ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 0);
> +}
> +
> +int init_afu(struct pci_dev *dev, struct ocxl_fn *fn, u8 afu_idx)
> +{
> + int rc;
> + struct ocxl_afu *afu;
> +
> + afu = alloc_afu(fn);
> + if (!afu)
> + return -ENOMEM;
> +
> + rc = configure_afu(afu, afu_idx, dev);
> + if (rc) {
> + free_afu(afu);
> + return rc;
> + }
> +
> + rc = ocxl_register_afu(afu);
> + if (rc)
> + goto err;
> +
> + rc = ocxl_sysfs_add_afu(afu);
> + if (rc)
> + goto err;
> +
> + rc = activate_afu(dev, afu);
> + if (rc)
> + goto err_sys;
> +
> + list_add_tail(&afu->list, &fn->afu_list);
> + return 0;
> +
> +err_sys:
> + ocxl_sysfs_remove_afu(afu);
> +err:
> + deconfigure_afu(afu);
> + device_unregister(&afu->dev);
> + return rc;
> +}
> +
> +void remove_afu(struct ocxl_afu *afu)
> +{
> + list_del(&afu->list);
> + ocxl_context_detach_all(afu);
> + deactivate_afu(afu);
> + ocxl_sysfs_remove_afu(afu);
> + deconfigure_afu(afu);
> + device_unregister(&afu->dev);
> +}
> +
> +static struct ocxl_fn *alloc_function(struct pci_dev *dev)
> +{
> + struct ocxl_fn *fn;
> +
> + fn = kzalloc(sizeof(struct ocxl_fn), GFP_KERNEL);
> + if (!fn)
> + return NULL;
> +
> + INIT_LIST_HEAD(&fn->afu_list);
> + INIT_LIST_HEAD(&fn->pasid_list);
> + INIT_LIST_HEAD(&fn->actag_list);
> + return fn;
> +}
> +
> +static void free_function(struct ocxl_fn *fn)
> +{
> + WARN_ON(!list_empty(&fn->afu_list));
> + WARN_ON(!list_empty(&fn->pasid_list));
> + kfree(fn);
> +}
> +
> +static void free_function_dev(struct device *dev)
> +{
> + struct ocxl_fn *fn = to_ocxl_function(dev);
> +
> + free_function(fn);
> +}
> +
> +static int set_function_device(struct ocxl_fn *fn, struct pci_dev *dev)
> +{
> + int rc;
> +
> + fn->dev.parent = &dev->dev;
> + fn->dev.release = free_function_dev;
> + rc = dev_set_name(&fn->dev, "ocxlfn.%s", dev_name(&dev->dev));
> + if (rc)
> + return rc;
> + pci_set_drvdata(dev, fn);
> + return 0;
> +}
> +
> +static int assign_function_actag(struct ocxl_fn *fn)
> +{
> + struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> + u16 base, enabled, supported;
> + int rc;
> +
> + rc = ocxl_config_get_actag_info(dev, &base, &enabled, &supported);
> + if (rc)
> + return rc;
> +
> + fn->actag_base = base;
> + fn->actag_enabled = enabled;
> + fn->actag_supported = supported;
> +
> + ocxl_config_set_actag(dev, fn->config.dvsec_function_pos,
> + fn->actag_base, fn->actag_enabled);
> + dev_dbg(&fn->dev, "actag range starting at %d, enabled %d\n",
> + fn->actag_base, fn->actag_enabled);
> + return 0;
> +}
> +
> +static int set_function_pasid(struct ocxl_fn *fn)
> +{
> + struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> + int rc, desired_count, max_count;
> +
> + /* A function may not require any PASID */
> + if (fn->config.max_pasid_log < 0)
> + return 0;
> +
> + rc = ocxl_config_get_pasid_info(dev, &max_count);
> + if (rc)
> + return rc;
> +
> + desired_count = 1 << fn->config.max_pasid_log;
> +
> + if (desired_count > max_count) {
> + dev_err(&fn->dev,
> + "Function requires more PASIDs than is available (%d vs. %d)\n",
> + desired_count, max_count);
> + return -ENOSPC;
> + }
> +
> + fn->pasid_base = 0;
> + return 0;
> +}
> +
> +static int configure_function(struct ocxl_fn *fn, struct pci_dev *dev)
> +{
> + int rc;
> +
> + rc = pci_enable_device(dev);
> + if (rc) {
> + dev_err(&dev->dev, "pci_enable_device failed: %d\n", rc);
> + return rc;
> + }
> +
> + /*
> + * Once it has been confirmed to work on our hardware, we
> + * should reset the function, to force the adapter to restart
> + * from scratch.
> + * A function reset would also reset all its AFUs.
> + *
> + * Some hints for implementation:
> + *
> + * - there's not status bit to know when the reset is done. We
> + * should try reading the config space to know when it's
> + * done.
> + * - probably something like:
> + * Reset
> + * wait 100ms
> + * issue config read
> + * allow device up to 1 sec to return success on config
> + * read before declaring it broken
> + *
> + * Some shared logic on the card (CFG, TLX) won't be reset, so
> + * there's no guarantee that it will be enough.
> + */
> + rc = ocxl_config_read_function(dev, &fn->config);
> + if (rc)
> + return rc;
> +
> + rc = set_function_device(fn, dev);
> + if (rc)
> + return rc;
> +
> + rc = assign_function_actag(fn);
> + if (rc)
> + return rc;
> +
> + rc = set_function_pasid(fn);
> + if (rc)
> + return rc;
> +
> + rc = ocxl_link_setup(dev, 0, &fn->link);
> + if (rc)
> + return rc;
> +
> + rc = ocxl_config_set_TL(dev, fn->config.dvsec_tl_pos);
> + if (rc) {
> + ocxl_link_release(dev, fn->link);
> + return rc;
> + }
> + return 0;
> +}
> +
> +static void deconfigure_function(struct ocxl_fn *fn)
> +{
> + struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> +
> + ocxl_link_release(dev, fn->link);
> + pci_disable_device(dev);
> +}
> +
> +struct ocxl_fn *init_function(struct pci_dev *dev)
> +{
> + struct ocxl_fn *fn;
> + int rc;
> +
> + fn = alloc_function(dev);
> + if (!fn)
> + return ERR_PTR(-ENOMEM);
> +
> + rc = configure_function(fn, dev);
> + if (rc) {
> + free_function(fn);
> + return ERR_PTR(rc);
> + }
> +
> + rc = device_register(&fn->dev);
> + if (rc) {
> + deconfigure_function(fn);
> + put_device(&fn->dev);
> + return ERR_PTR(rc);
> + }
> + return fn;
> +}
> +
> +void remove_function(struct ocxl_fn *fn)
> +{
> + deconfigure_function(fn);
> + device_unregister(&fn->dev);
> +}
> diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
> index 06fd98c989c8..81086534dab5 100644
> --- a/drivers/misc/ocxl/ocxl_internal.h
> +++ b/drivers/misc/ocxl/ocxl_internal.h
> @@ -150,4 +150,9 @@ int ocxl_afu_irq_set_fd(struct ocxl_context *ctx, u64 irq_offset,
> int eventfd);
> u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, u64 irq_offset);
>
> +struct ocxl_fn *init_function(struct pci_dev *dev);
> +void remove_function(struct ocxl_fn *fn);
> +int init_afu(struct pci_dev *dev, struct ocxl_fn *fn, u8 afu_idx);
> +void remove_afu(struct ocxl_afu *afu);
> +
> #endif /* _OCXL_INTERNAL_H_ */
> diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
> index 21f425472a82..4ed7cb1a667f 100644
> --- a/drivers/misc/ocxl/pci.c
> +++ b/drivers/misc/ocxl/pci.c
> @@ -1,9 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0+
> -// Copyright 2017 IBM Corp.
> +// Copyright 2019 IBM Corp.
> #include <linux/module.h>
> -#include <linux/pci.h>
> -#include <linux/idr.h>
> -#include <asm/pnv-ocxl.h>
> #include "ocxl_internal.h"
>
> /*
> @@ -17,520 +14,6 @@ static const struct pci_device_id ocxl_pci_tbl[] = {
> };
> MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);
>
> -
> -static struct ocxl_fn *ocxl_fn_get(struct ocxl_fn *fn)
> -{
> - return (get_device(&fn->dev) == NULL) ? NULL : fn;
> -}
> -
> -static void ocxl_fn_put(struct ocxl_fn *fn)
> -{
> - put_device(&fn->dev);
> -}
> -
> -struct ocxl_afu *ocxl_afu_get(struct ocxl_afu *afu)
> -{
> - return (get_device(&afu->dev) == NULL) ? NULL : afu;
> -}
> -
> -void ocxl_afu_put(struct ocxl_afu *afu)
> -{
> - put_device(&afu->dev);
> -}
> -
> -static struct ocxl_afu *alloc_afu(struct ocxl_fn *fn)
> -{
> - struct ocxl_afu *afu;
> -
> - afu = kzalloc(sizeof(struct ocxl_afu), GFP_KERNEL);
> - if (!afu)
> - return NULL;
> -
> - mutex_init(&afu->contexts_lock);
> - mutex_init(&afu->afu_control_lock);
> - idr_init(&afu->contexts_idr);
> - afu->fn = fn;
> - ocxl_fn_get(fn);
> - return afu;
> -}
> -
> -static void free_afu(struct ocxl_afu *afu)
> -{
> - idr_destroy(&afu->contexts_idr);
> - ocxl_fn_put(afu->fn);
> - kfree(afu);
> -}
> -
> -static void free_afu_dev(struct device *dev)
> -{
> - struct ocxl_afu *afu = to_ocxl_afu(dev);
> -
> - ocxl_unregister_afu(afu);
> - free_afu(afu);
> -}
> -
> -static int set_afu_device(struct ocxl_afu *afu, const char *location)
> -{
> - struct ocxl_fn *fn = afu->fn;
> - int rc;
> -
> - afu->dev.parent = &fn->dev;
> - afu->dev.release = free_afu_dev;
> - rc = dev_set_name(&afu->dev, "%s.%s.%hhu", afu->config.name, location,
> - afu->config.idx);
> - return rc;
> -}
> -
> -static int assign_afu_actag(struct ocxl_afu *afu, struct pci_dev *dev)
> -{
> - struct ocxl_fn *fn = afu->fn;
> - int actag_count, actag_offset;
> -
> - /*
> - * if there were not enough actags for the function, each afu
> - * reduces its count as well
> - */
> - actag_count = afu->config.actag_supported *
> - fn->actag_enabled / fn->actag_supported;
> - actag_offset = ocxl_actag_afu_alloc(fn, actag_count);
> - if (actag_offset < 0) {
> - dev_err(&afu->dev, "Can't allocate %d actags for AFU: %d\n",
> - actag_count, actag_offset);
> - return actag_offset;
> - }
> - afu->actag_base = fn->actag_base + actag_offset;
> - afu->actag_enabled = actag_count;
> -
> - ocxl_config_set_afu_actag(dev, afu->config.dvsec_afu_control_pos,
> - afu->actag_base, afu->actag_enabled);
> - dev_dbg(&afu->dev, "actag base=%d enabled=%d\n",
> - afu->actag_base, afu->actag_enabled);
> - return 0;
> -}
> -
> -static void reclaim_afu_actag(struct ocxl_afu *afu)
> -{
> - struct ocxl_fn *fn = afu->fn;
> - int start_offset, size;
> -
> - start_offset = afu->actag_base - fn->actag_base;
> - size = afu->actag_enabled;
> - ocxl_actag_afu_free(afu->fn, start_offset, size);
> -}
> -
> -static int assign_afu_pasid(struct ocxl_afu *afu, struct pci_dev *dev)
> -{
> - struct ocxl_fn *fn = afu->fn;
> - int pasid_count, pasid_offset;
> -
> - /*
> - * We only support the case where the function configuration
> - * requested enough PASIDs to cover all AFUs.
> - */
> - pasid_count = 1 << afu->config.pasid_supported_log;
> - pasid_offset = ocxl_pasid_afu_alloc(fn, pasid_count);
> - if (pasid_offset < 0) {
> - dev_err(&afu->dev, "Can't allocate %d PASIDs for AFU: %d\n",
> - pasid_count, pasid_offset);
> - return pasid_offset;
> - }
> - afu->pasid_base = fn->pasid_base + pasid_offset;
> - afu->pasid_count = 0;
> - afu->pasid_max = pasid_count;
> -
> - ocxl_config_set_afu_pasid(dev, afu->config.dvsec_afu_control_pos,
> - afu->pasid_base,
> - afu->config.pasid_supported_log);
> - dev_dbg(&afu->dev, "PASID base=%d, enabled=%d\n",
> - afu->pasid_base, pasid_count);
> - return 0;
> -}
> -
> -static void reclaim_afu_pasid(struct ocxl_afu *afu)
> -{
> - struct ocxl_fn *fn = afu->fn;
> - int start_offset, size;
> -
> - start_offset = afu->pasid_base - fn->pasid_base;
> - size = 1 << afu->config.pasid_supported_log;
> - ocxl_pasid_afu_free(afu->fn, start_offset, size);
> -}
> -
> -static int reserve_fn_bar(struct ocxl_fn *fn, int bar)
> -{
> - struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> - int rc, idx;
> -
> - if (bar != 0 && bar != 2 && bar != 4)
> - return -EINVAL;
> -
> - idx = bar >> 1;
> - if (fn->bar_used[idx]++ == 0) {
> - rc = pci_request_region(dev, bar, "ocxl");
> - if (rc)
> - return rc;
> - }
> - return 0;
> -}
> -
> -static void release_fn_bar(struct ocxl_fn *fn, int bar)
> -{
> - struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> - int idx;
> -
> - if (bar != 0 && bar != 2 && bar != 4)
> - return;
> -
> - idx = bar >> 1;
> - if (--fn->bar_used[idx] == 0)
> - pci_release_region(dev, bar);
> - WARN_ON(fn->bar_used[idx] < 0);
> -}
> -
> -static int map_mmio_areas(struct ocxl_afu *afu, struct pci_dev *dev)
> -{
> - int rc;
> -
> - rc = reserve_fn_bar(afu->fn, afu->config.global_mmio_bar);
> - if (rc)
> - return rc;
> -
> - rc = reserve_fn_bar(afu->fn, afu->config.pp_mmio_bar);
> - if (rc) {
> - release_fn_bar(afu->fn, afu->config.global_mmio_bar);
> - return rc;
> - }
> -
> - afu->global_mmio_start =
> - pci_resource_start(dev, afu->config.global_mmio_bar) +
> - afu->config.global_mmio_offset;
> - afu->pp_mmio_start =
> - pci_resource_start(dev, afu->config.pp_mmio_bar) +
> - afu->config.pp_mmio_offset;
> -
> - afu->global_mmio_ptr = ioremap(afu->global_mmio_start,
> - afu->config.global_mmio_size);
> - if (!afu->global_mmio_ptr) {
> - release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
> - release_fn_bar(afu->fn, afu->config.global_mmio_bar);
> - dev_err(&dev->dev, "Error mapping global mmio area\n");
> - return -ENOMEM;
> - }
> -
> - /*
> - * Leave an empty page between the per-process mmio area and
> - * the AFU interrupt mappings
> - */
> - afu->irq_base_offset = afu->config.pp_mmio_stride + PAGE_SIZE;
> - return 0;
> -}
> -
> -static void unmap_mmio_areas(struct ocxl_afu *afu)
> -{
> - if (afu->global_mmio_ptr) {
> - iounmap(afu->global_mmio_ptr);
> - afu->global_mmio_ptr = NULL;
> - }
> - afu->global_mmio_start = 0;
> - afu->pp_mmio_start = 0;
> - release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
> - release_fn_bar(afu->fn, afu->config.global_mmio_bar);
> -}
> -
> -static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
> -{
> - int rc;
> -
> - rc = ocxl_config_read_afu(dev, &afu->fn->config, &afu->config, afu_idx);
> - if (rc)
> - return rc;
> -
> - rc = set_afu_device(afu, dev_name(&dev->dev));
> - if (rc)
> - return rc;
> -
> - rc = assign_afu_actag(afu, dev);
> - if (rc)
> - return rc;
> -
> - rc = assign_afu_pasid(afu, dev);
> - if (rc) {
> - reclaim_afu_actag(afu);
> - return rc;
> - }
> -
> - rc = map_mmio_areas(afu, dev);
> - if (rc) {
> - reclaim_afu_pasid(afu);
> - reclaim_afu_actag(afu);
> - return rc;
> - }
> - return 0;
> -}
> -
> -static void deconfigure_afu(struct ocxl_afu *afu)
> -{
> - unmap_mmio_areas(afu);
> - reclaim_afu_pasid(afu);
> - reclaim_afu_actag(afu);
> -}
> -
> -static int activate_afu(struct pci_dev *dev, struct ocxl_afu *afu)
> -{
> - int rc;
> -
> - ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 1);
> - /*
> - * Char device creation is the last step, as processes can
> - * call our driver immediately, so all our inits must be finished.
> - */
> - rc = ocxl_create_cdev(afu);
> - if (rc)
> - return rc;
> - return 0;
> -}
> -
> -static void deactivate_afu(struct ocxl_afu *afu)
> -{
> - struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
> -
> - ocxl_destroy_cdev(afu);
> - ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 0);
> -}
> -
> -static int init_afu(struct pci_dev *dev, struct ocxl_fn *fn, u8 afu_idx)
> -{
> - int rc;
> - struct ocxl_afu *afu;
> -
> - afu = alloc_afu(fn);
> - if (!afu)
> - return -ENOMEM;
> -
> - rc = configure_afu(afu, afu_idx, dev);
> - if (rc) {
> - free_afu(afu);
> - return rc;
> - }
> -
> - rc = ocxl_register_afu(afu);
> - if (rc)
> - goto err;
> -
> - rc = ocxl_sysfs_add_afu(afu);
> - if (rc)
> - goto err;
> -
> - rc = activate_afu(dev, afu);
> - if (rc)
> - goto err_sys;
> -
> - list_add_tail(&afu->list, &fn->afu_list);
> - return 0;
> -
> -err_sys:
> - ocxl_sysfs_remove_afu(afu);
> -err:
> - deconfigure_afu(afu);
> - device_unregister(&afu->dev);
> - return rc;
> -}
> -
> -static void remove_afu(struct ocxl_afu *afu)
> -{
> - list_del(&afu->list);
> - ocxl_context_detach_all(afu);
> - deactivate_afu(afu);
> - ocxl_sysfs_remove_afu(afu);
> - deconfigure_afu(afu);
> - device_unregister(&afu->dev);
> -}
> -
> -static struct ocxl_fn *alloc_function(struct pci_dev *dev)
> -{
> - struct ocxl_fn *fn;
> -
> - fn = kzalloc(sizeof(struct ocxl_fn), GFP_KERNEL);
> - if (!fn)
> - return NULL;
> -
> - INIT_LIST_HEAD(&fn->afu_list);
> - INIT_LIST_HEAD(&fn->pasid_list);
> - INIT_LIST_HEAD(&fn->actag_list);
> - return fn;
> -}
> -
> -static void free_function(struct ocxl_fn *fn)
> -{
> - WARN_ON(!list_empty(&fn->afu_list));
> - WARN_ON(!list_empty(&fn->pasid_list));
> - kfree(fn);
> -}
> -
> -static void free_function_dev(struct device *dev)
> -{
> - struct ocxl_fn *fn = to_ocxl_function(dev);
> -
> - free_function(fn);
> -}
> -
> -static int set_function_device(struct ocxl_fn *fn, struct pci_dev *dev)
> -{
> - int rc;
> -
> - fn->dev.parent = &dev->dev;
> - fn->dev.release = free_function_dev;
> - rc = dev_set_name(&fn->dev, "ocxlfn.%s", dev_name(&dev->dev));
> - if (rc)
> - return rc;
> - pci_set_drvdata(dev, fn);
> - return 0;
> -}
> -
> -static int assign_function_actag(struct ocxl_fn *fn)
> -{
> - struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> - u16 base, enabled, supported;
> - int rc;
> -
> - rc = ocxl_config_get_actag_info(dev, &base, &enabled, &supported);
> - if (rc)
> - return rc;
> -
> - fn->actag_base = base;
> - fn->actag_enabled = enabled;
> - fn->actag_supported = supported;
> -
> - ocxl_config_set_actag(dev, fn->config.dvsec_function_pos,
> - fn->actag_base, fn->actag_enabled);
> - dev_dbg(&fn->dev, "actag range starting at %d, enabled %d\n",
> - fn->actag_base, fn->actag_enabled);
> - return 0;
> -}
> -
> -static int set_function_pasid(struct ocxl_fn *fn)
> -{
> - struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> - int rc, desired_count, max_count;
> -
> - /* A function may not require any PASID */
> - if (fn->config.max_pasid_log < 0)
> - return 0;
> -
> - rc = ocxl_config_get_pasid_info(dev, &max_count);
> - if (rc)
> - return rc;
> -
> - desired_count = 1 << fn->config.max_pasid_log;
> -
> - if (desired_count > max_count) {
> - dev_err(&fn->dev,
> - "Function requires more PASIDs than is available (%d vs. %d)\n",
> - desired_count, max_count);
> - return -ENOSPC;
> - }
> -
> - fn->pasid_base = 0;
> - return 0;
> -}
> -
> -static int configure_function(struct ocxl_fn *fn, struct pci_dev *dev)
> -{
> - int rc;
> -
> - rc = pci_enable_device(dev);
> - if (rc) {
> - dev_err(&dev->dev, "pci_enable_device failed: %d\n", rc);
> - return rc;
> - }
> -
> - /*
> - * Once it has been confirmed to work on our hardware, we
> - * should reset the function, to force the adapter to restart
> - * from scratch.
> - * A function reset would also reset all its AFUs.
> - *
> - * Some hints for implementation:
> - *
> - * - there's not status bit to know when the reset is done. We
> - * should try reading the config space to know when it's
> - * done.
> - * - probably something like:
> - * Reset
> - * wait 100ms
> - * issue config read
> - * allow device up to 1 sec to return success on config
> - * read before declaring it broken
> - *
> - * Some shared logic on the card (CFG, TLX) won't be reset, so
> - * there's no guarantee that it will be enough.
> - */
> - rc = ocxl_config_read_function(dev, &fn->config);
> - if (rc)
> - return rc;
> -
> - rc = set_function_device(fn, dev);
> - if (rc)
> - return rc;
> -
> - rc = assign_function_actag(fn);
> - if (rc)
> - return rc;
> -
> - rc = set_function_pasid(fn);
> - if (rc)
> - return rc;
> -
> - rc = ocxl_link_setup(dev, 0, &fn->link);
> - if (rc)
> - return rc;
> -
> - rc = ocxl_config_set_TL(dev, fn->config.dvsec_tl_pos);
> - if (rc) {
> - ocxl_link_release(dev, fn->link);
> - return rc;
> - }
> - return 0;
> -}
> -
> -static void deconfigure_function(struct ocxl_fn *fn)
> -{
> - struct pci_dev *dev = to_pci_dev(fn->dev.parent);
> -
> - ocxl_link_release(dev, fn->link);
> - pci_disable_device(dev);
> -}
> -
> -static struct ocxl_fn *init_function(struct pci_dev *dev)
> -{
> - struct ocxl_fn *fn;
> - int rc;
> -
> - fn = alloc_function(dev);
> - if (!fn)
> - return ERR_PTR(-ENOMEM);
> -
> - rc = configure_function(fn, dev);
> - if (rc) {
> - free_function(fn);
> - return ERR_PTR(rc);
> - }
> -
> - rc = device_register(&fn->dev);
> - if (rc) {
> - deconfigure_function(fn);
> - put_device(&fn->dev);
> - return ERR_PTR(rc);
> - }
> - return fn;
> -}
> -
> -static void remove_function(struct ocxl_fn *fn)
> -{
> - deconfigure_function(fn);
> - device_unregister(&fn->dev);
> -}
> -
> static int ocxl_probe(struct pci_dev *dev, const struct pci_device_id *id)
> {
> int rc, afu_count = 0;
>
Powered by blists - more mailing lists