[PATCH 20/23] net: hpe: ss1: add the CXI core interfaces
flat view
HOTtoday
From: Doug Ledford <hidden>
Date: 2026-10-30 21:01:50
Also in:
linux-kbuild, lkml
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Add the CXI core interface: client registration, the auxiliary device used by the RDMA driver, link control and the configuration tables. Known limitation, deferred to V2: this patch carries SR-IOV virtual-function code (VF-to-PF mailbox requests, mailbox opcodes or VF resource handling) from an older, incomplete version of the Cassini SR-IOV support. V2 of this series will include the updated SR-IOV implementation, so issues in the VF paths should be ignored for now. checkpatch --strict (100 columns) still reports the following in this patch. They need coordination with the internal repositories this code is maintained in and will be fixed in V2: - WARNING: ALLOC_WITH_SIZEOF (23), LONG_LINE (4), SPDX_LICENSE_TAG (4), BLOCK_COMMENT_STYLE (1), BRACES (1), MACRO_WITH_FLOW_CONTROL (1), MODULE_LICENSE (1) - CHECK: ALLOC_SIZEOF_STRUCT (2) Co-developed-by: Sandeep Bhoot <redacted> Signed-off-by: Sandeep Bhoot <redacted> Co-developed-by: Mamila Santosh <redacted> Signed-off-by: Mamila Santosh <redacted> Signed-off-by: Doug Ledford <redacted> --- drivers/net/ethernet/hpe/ss1/cxi_aux_dev.c | 137 ++ drivers/net/ethernet/hpe/ss1/cxi_config.c | 2990 ++++++++++++++++++++++++++++ drivers/net/ethernet/hpe/ss1/cxi_core.c | 281 +++ drivers/net/ethernet/hpe/ss1/cxi_link.c | 227 +++ 4 files changed, 3635 insertions(+)
diff --git a/drivers/net/ethernet/hpe/ss1/cxi_aux_dev.c b/drivers/net/ethernet/hpe/ss1/cxi_aux_dev.c
new file mode 100644
index 000000000000..74f20e156d5e
--- /dev/null
+++ b/drivers/net/ethernet/hpe/ss1/cxi_aux_dev.c@@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0*/ +/* Copyright 2026 Hewlett Packard Enterprise Development LP */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/hpe/cxi/cxi.h> +#include <linux/hpe/cxi/driver.h> +#include <linux/hpe/cxi/cxi_prov_hw.h> +#include "cass_core.h" +#include "cxi_aux_dev.h" +#include "cxi_internal.h" + +void cxi_release_aux_dev(struct device *dev) +{ + struct cxi_adev *cadev = container_of(dev, struct cxi_adev, adev.dev); + + kfree(cadev); +} + +/* Stop publishing the auxiliary device and wait for anyone using it. */ +static void cxi_ib_unpublish_adev(struct cass_dev *hw) +{ + mutex_lock(&hw->cxi_adev_lock); + hw->cxi_adev = NULL; + mutex_unlock(&hw->cxi_adev_lock); +} + +void cxi_ib_remove_adev(struct cass_dev *hw) +{ + struct cxi_adev *cadev; + + /* + * Unpublish first. A NID change in progress holds the lock, so once + * this returns nothing can still be using the device, and nothing new + * can find it. + */ + mutex_lock(&hw->cxi_adev_lock); + cadev = hw->cxi_adev; + hw->cxi_adev = NULL; + mutex_unlock(&hw->cxi_adev_lock); + + if (!cadev) + return; + + auxiliary_device_delete(&cadev->adev); + auxiliary_device_uninit(&cadev->adev); +} + +u8 *cxi_get_mac_addr(struct cxi_dev *dev) +{ + return dev->mac_addr; +} + +int cxi_ib_add_adev(struct pci_dev *pdev, struct cass_dev *hw, int cxi_num) +{ + struct auxiliary_device *adev; + struct cxi_adev *cadev; + int rc; + + cadev = kzalloc(sizeof(*cadev), GFP_KERNEL); + if (!cadev) + return -ENOMEM; + + cadev->dev = &pdev->dev; + cadev->pdev = pdev; + cadev->cxi_dev = &hw->cdev; + BLOCKING_INIT_NOTIFIER_HEAD(&cadev->nid_notifier); + cadev->lni_alloc = cxi_lni_alloc; + cadev->svc_enable = cxi_svc_enable; + cadev->svc_set_vni_range = cxi_svc_set_vni_range; + cadev->svc_get_vni_range = cxi_svc_get_vni_range; + cadev->tc_cfg = cxi_tc_cfg; + cadev->pid_alloc = cxi_lni_pid_alloc; + cadev->pid_free = cxi_lni_pid_free; + cadev->lni_free = cxi_lni_free; + cadev->query_device = NULL; /* optional */ + cadev->cxi_map = cxi_map; + cadev->dev_info_get = cxi_dev_info_get; + cadev->is_link_up = cxi_is_link_up; + cadev->svc_get = cxi_svc_get; + cadev->svc_rsrc_get = cxi_svc_rsrc_get; + cadev->svc_alloc = cxi_svc_alloc; + cadev->svc_destroy = cxi_svc_destroy; + cadev->job_tc_bind = cxi_job_tc_bind; + cadev->tc_clear = cxi_tc_clear; + cadev->tc_list = cxi_tc_list; + cadev->domain_reserve = cxi_domain_reserve; + cadev->domain_alloc = cxi_domain_alloc; + cadev->domain_free = cxi_domain_free; + cadev->cp_alloc = cxi_cp_alloc; + cadev->cp_free = cxi_cp_free; + cadev->cq_alloc = cxi_cq_alloc; + cadev->cq_free = cxi_cq_free; + cadev->cxi_pte_alloc = cxi_pte_alloc; + cadev->cq_user_info = cxi_cq_user_info; + cadev->eq_alloc = cxi_eq_alloc; + cadev->eq_free = cxi_eq_free; + cadev->eq_get_event = cxi_eq_get_event; + cadev->eq_next_event = cxi_eq_next_event; + cadev->cxi_unmap = cxi_unmap; + cadev->cxi_pte_free = cxi_pte_free; + cadev->ct_alloc = cxi_ct_alloc; + cadev->ct_free = cxi_ct_free; + cadev->clear_md = cxi_clear_md; + cadev->eq_user_info = cxi_eq_user_info; + cadev->ct_user_info = cxi_ct_user_info; + cadev->pte_map = cxi_pte_map; + cadev->pte_unmap = cxi_pte_unmap; + cadev->get_mac_addr = cxi_get_mac_addr; + + adev = &cadev->adev; + adev->name = "rdma"; + adev->id = cxi_num; + adev->dev.parent = &pdev->dev; + adev->dev.release = cxi_release_aux_dev; + + mutex_lock(&hw->cxi_adev_lock); + hw->cxi_adev = cadev; + mutex_unlock(&hw->cxi_adev_lock); + + rc = auxiliary_device_init(adev); + if (rc) { + cxi_ib_unpublish_adev(hw); + kfree(cadev); + return rc; + } + + rc = auxiliary_device_add(adev); + if (rc) { + cxi_ib_unpublish_adev(hw); + auxiliary_device_uninit(adev); /* release() frees cadev */ + return rc; + } + + return 0; +} +
diff --git a/drivers/net/ethernet/hpe/ss1/cxi_config.c b/drivers/net/ethernet/hpe/ss1/cxi_config.c
new file mode 100644
index 000000000000..dd604753daee
--- /dev/null
+++ b/drivers/net/ethernet/hpe/ss1/cxi_config.c@@ -0,0 +1,2990 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Copyright 2025 Hewlett Packard Enterprise Development LP */ + +/* + * Configuration and Management of rgroup/rx profile/tx profile using + * configfs Filesystem Interface + */ + +#include "cxi_config.h" + +struct cxi_cfg_teardown_marker { + struct list_head list; + struct config_item *item; +}; + +static LIST_HEAD(cxi_cfg_teardown_markers); +static DEFINE_SPINLOCK(cxi_cfg_teardown_lock); + +static void cxi_cfg_drop_item_ref(struct config_item *item) +{ + struct cxi_cfg_teardown_marker *marker; + unsigned long flags; + bool teardown_owns_ref = false; + + spin_lock_irqsave(&cxi_cfg_teardown_lock, flags); + list_for_each_entry(marker, &cxi_cfg_teardown_markers, list) { + if (marker->item == item) { + teardown_owns_ref = true; + break; + } + } + spin_unlock_irqrestore(&cxi_cfg_teardown_lock, flags); + + if (!teardown_owns_ref) + config_item_put(item); +} + +static enum cxi_resource_type cxi_resource_str_to_type(const char *name) +{ + int i; + + for (i = CXI_RESOURCE_PTLTE; i < CXI_RESOURCE_MAX; i++) { + if (strcmp(name, cxi_resource_type_strs[i]) == 0) + return i; + } + + return CXI_RESOURCE_MAX; +} + +static struct config_group *get_parent_config_group(struct config_group *child_group) +{ + struct config_group *parent_config_group; + + if (!child_group->cg_item.ci_parent) + return NULL; + + parent_config_group = container_of(child_group->cg_item.ci_parent, + struct config_group, + cg_item); + + return parent_config_group; +} + +static const int resource_max[CXI_RESOURCE_MAX] = { + [CXI_RESOURCE_PTLTE] = C_NUM_PTLTES, + [CXI_RESOURCE_TXQ] = C_NUM_TRANSMIT_CQS, + [CXI_RESOURCE_TGQ] = C_NUM_TARGET_CQS, + [CXI_RESOURCE_EQ] = EQS_AVAIL, + [CXI_RESOURCE_CT] = CTS_AVAIL, + [CXI_RESOURCE_PE0_LE] = PE_LE_MAX, + [CXI_RESOURCE_PE1_LE] = PE_LE_MAX, + [CXI_RESOURCE_PE2_LE] = PE_LE_MAX, + [CXI_RESOURCE_PE3_LE] = PE_LE_MAX, + [CXI_RESOURCE_TLE] = 0, + [CXI_RESOURCE_AC] = ACS_AVAIL, +}; + +static struct cxi_cfg_rgroup_ac_entry_sub *to_cxi_cfg_rgroup_ac_entry_sub(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rgroup_ac_entry_sub, group); +} + +static ssize_t cxi_cfg_rgroup_ac_entry_sub_type_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_sub; + int rc; + enum cxi_ac_type ac_type; + union cxi_ac_data ac_data; + + ac_entry_sub = to_cxi_cfg_rgroup_ac_entry_sub(item); + + rc = cxi_rgroup_get_ac_entry_data(ac_entry_sub->rgroup, + ac_entry_sub->id, + &ac_type, + &ac_data); + if (rc) + return rc; + + return sprintf(page, "%d\n", ac_type); +} + +static ssize_t cxi_cfg_rgroup_ac_entry_sub_type_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_sub; + int rc; + enum cxi_ac_type ac_type_val; + union cxi_ac_data ac_data_t = {}; + unsigned int ac_entry_id; + + ac_entry_sub = to_cxi_cfg_rgroup_ac_entry_sub(item); + if (!ac_entry_sub->rgroup) { + pr_debug("Unable to get rgroup reference\n"); + return -EINVAL; + } + + rc = kstrtouint(page, 0, (unsigned int *)&ac_type_val); + if (rc) + return rc; + + if (ac_type_val == CXI_AC_UID) + ac_data_t.uid = ac_entry_sub->ac_data.uid; + else if (ac_type_val == CXI_AC_GID) + ac_data_t.gid = ac_entry_sub->ac_data.gid; + else if (ac_type_val == CXI_AC_NETNS) + ac_data_t.netns = ac_entry_sub->ac_data.netns; + else if (ac_type_val == CXI_AC_OPEN) + pr_debug("Setting type to CXI_AC_OPEN\n"); + else + return -EINVAL; + + rc = cxi_rgroup_add_ac_entry(ac_entry_sub->rgroup, + ac_type_val, + &ac_data_t, + &ac_entry_id); + if (rc < 0) { + pr_debug("Unable to add ac entry to resource group: err val %d\n", rc); + return rc; + } + + ac_entry_sub->ac_type = ac_type_val; + ac_entry_sub->id = ac_entry_id; + + return count; +} + +static ssize_t cxi_cfg_rgroup_ac_entry_sub_data_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_sub; + enum cxi_ac_type ac_type; + union cxi_ac_data ac_data; + int rc; + + ac_entry_sub = to_cxi_cfg_rgroup_ac_entry_sub(item); + + rc = cxi_rgroup_get_ac_entry_data(ac_entry_sub->rgroup, + ac_entry_sub->id, + &ac_type, + &ac_data); + if (rc) + return rc; + + if (ac_type == CXI_AC_UID) + return sprintf(page, "%d\n", ac_data.uid); + else if (ac_type == CXI_AC_GID) + return sprintf(page, "%d\n", ac_data.gid); + else if (ac_type == CXI_AC_NETNS) + return sprintf(page, "%d\n", ac_data.netns); + else if (ac_type == CXI_AC_OPEN) + return sprintf(page, "%d\n", CXI_AC_OPEN); + + return -EINVAL; +} + +static ssize_t cxi_cfg_rgroup_ac_entry_sub_data_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_sub; + int rc; + unsigned int ac_data_val; + + ac_entry_sub = to_cxi_cfg_rgroup_ac_entry_sub(item); + + rc = kstrtouint(page, 0, &ac_data_val); + if (rc) + return rc; + + if (ac_entry_sub->ac_type == CXI_AC_UID) { + ac_entry_sub->ac_data.uid = ac_data_val; + return count; + } else if (ac_entry_sub->ac_type == CXI_AC_GID) { + ac_entry_sub->ac_data.gid = ac_data_val; + return count; + } else if (ac_entry_sub->ac_type == CXI_AC_NETNS) { + ac_entry_sub->ac_data.netns = ac_data_val; + return count; + } + + return -EINVAL; +} + +static void cxi_cfg_rgroup_ac_entry_sub_free(struct config_item *item) +{ + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_sub; + + ac_entry_sub = container_of(item, struct cxi_cfg_rgroup_ac_entry_sub, + group.cg_item); + kfree(ac_entry_sub); +} + +CONFIGFS_ATTR(cxi_cfg_rgroup_ac_entry_sub_, type); +CONFIGFS_ATTR(cxi_cfg_rgroup_ac_entry_sub_, data); + +static struct configfs_attribute *cxi_cfg_rgroup_ac_entry_sub_attrs[] = { + &cxi_cfg_rgroup_ac_entry_sub_attr_type, + &cxi_cfg_rgroup_ac_entry_sub_attr_data, + NULL, +}; + +static struct configfs_item_operations cxi_cfg_rgroup_ac_entry_sub_item_ops = { + .release = cxi_cfg_rgroup_ac_entry_sub_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_ac_entry_sub_type = { + .ct_attrs = cxi_cfg_rgroup_ac_entry_sub_attrs, + .ct_item_ops = &cxi_cfg_rgroup_ac_entry_sub_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rgroup_ac_entry_sub *alloc_dev_rgroup_ac_entry_sub(const char *name) +{ + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_dir_sub; + + ac_entry_dir_sub = kzalloc(sizeof(*ac_entry_dir_sub), + GFP_KERNEL); + if (!ac_entry_dir_sub) + return NULL; + + strscpy(ac_entry_dir_sub->name, name, sizeof(ac_entry_dir_sub->name)); + + config_group_init_type_name(&ac_entry_dir_sub->group, + ac_entry_dir_sub->name, + &cxi_cfg_rgroup_ac_entry_sub_type); + + return ac_entry_dir_sub; +} + +static struct +config_group *cxi_cfg_rgroup_ac_entry_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_rgroup_ac_entry *ac_entry; + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_sub; + + if (strlen(name) > CONFIGFS_DIR_LEN - 1) + return ERR_PTR(-EINVAL); + + ac_entry = container_of(group, struct cxi_cfg_rgroup_ac_entry, group); + + ac_entry_sub = alloc_dev_rgroup_ac_entry_sub(name); + if (!ac_entry_sub) { + pr_debug("Failed to alloc memory for configfs ac entry"); + return ERR_PTR(-ENOMEM); + } + + ac_entry_sub->rgroup = ac_entry->rgroup; + ac_entry_sub->ac_type = CXI_AC_UID; + ac_entry_sub->ac_data.uid = 0; + + return &ac_entry_sub->group; +} + +static void +cxi_cfg_rgroup_ac_entry_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cxi_cfg_rgroup_ac_entry_sub *ac_entry_sub; + unsigned int ac_entry_id; + int rc; + + ac_entry_sub = container_of(item, struct cxi_cfg_rgroup_ac_entry_sub, group.cg_item); + + if (ac_entry_sub->ac_type == CXI_AC_UID) { + rc = cxi_rgroup_get_ac_entry_id_by_data(ac_entry_sub->rgroup, + CXI_AC_UID, + &ac_entry_sub->ac_data, + &ac_entry_id); + + pr_debug("Getting an entry info rc = %d\n", rc); + } else if (ac_entry_sub->ac_type == CXI_AC_GID) { + rc = cxi_rgroup_get_ac_entry_id_by_data(ac_entry_sub->rgroup, + CXI_AC_GID, + &ac_entry_sub->ac_data, + &ac_entry_id); + + pr_debug("Getting an entry info rc = %d\n", rc); + } else if (ac_entry_sub->ac_type == CXI_AC_NETNS) { + rc = cxi_rgroup_get_ac_entry_id_by_data(ac_entry_sub->rgroup, + CXI_AC_NETNS, + &ac_entry_sub->ac_data, + &ac_entry_id); + pr_debug("Getting an entry info rc = %d\n", rc); + } else { + rc = cxi_rgroup_get_ac_entry_id_by_data(ac_entry_sub->rgroup, + CXI_AC_OPEN, + NULL, + &ac_entry_id); + pr_debug("Getting an entry info rc = %d\n", rc); + } + + if (!rc) + cxi_rgroup_delete_ac_entry(ac_entry_sub->rgroup, + ac_entry_id); + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_rgroup_ac_entry_free(struct config_item *item) +{ + struct cxi_cfg_rgroup_ac_entry *ac_entry; + + ac_entry = container_of(item, struct cxi_cfg_rgroup_ac_entry, + group.cg_item); + + kfree(ac_entry); +} + +static struct configfs_group_operations cxi_cfg_rgroup_ac_entry_ops = { + .make_group = cxi_cfg_rgroup_ac_entry_make_group, + .drop_item = cxi_cfg_rgroup_ac_entry_drop_item +}; + +static struct configfs_item_operations cxi_cfg_rgroup_ac_entry_item_ops = { + .release = cxi_cfg_rgroup_ac_entry_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_ac_entry_type = { + .ct_group_ops = &cxi_cfg_rgroup_ac_entry_ops, + .ct_item_ops = &cxi_cfg_rgroup_ac_entry_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rgroup_res_type *to_cxi_cfg_rgroup_res_type(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rgroup_res_type, group); +} + +static ssize_t cxi_cfg_rgroup_res_type_max_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_res_type *rgroup_res_type; + struct cxi_resource_entry *entry; + int rc; + + rgroup_res_type = to_cxi_cfg_rgroup_res_type(item); + rc = cxi_rgroup_get_resource_entry(rgroup_res_type->rgroup, + cxi_resource_str_to_type(rgroup_res_type->name), + &entry); + if (rc != 0) { + pr_debug("Error: Resource entry not yet created for the resource type\n"); + pr_debug("Error: Set the max file first and then the reserved file to create the resource type\n"); + return -EINVAL; + } + + return sprintf(page, "%ld\n", entry->limits.max); +} + +static ssize_t cxi_cfg_rgroup_res_type_max_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rgroup_res_type *rgroup_res_type; + struct cxi_resource_entry *entry; + int rc; + + rgroup_res_type = to_cxi_cfg_rgroup_res_type(item); + + rc = cxi_rgroup_get_resource_entry(rgroup_res_type->rgroup, + cxi_resource_str_to_type(rgroup_res_type->name), + &entry); + if (rc == 0) { + pr_debug("Error: Given resource type has already been added\n"); + return -EINVAL; + } + + rc = kstrtoint(page, 0, &rgroup_res_type->max); + if (rc) + return rc; + + pr_debug("resource max value set to: %d\n", rgroup_res_type->max); + + return count; +} + +static ssize_t cxi_cfg_rgroup_res_type_reserved_show(struct config_item *item, + char *page) +{ + int rc; + struct cxi_cfg_rgroup_res_type *rgroup_res_type; + struct cxi_resource_entry *entry; + + rgroup_res_type = to_cxi_cfg_rgroup_res_type(item); + + rc = cxi_rgroup_get_resource_entry(rgroup_res_type->rgroup, + cxi_resource_str_to_type(rgroup_res_type->name), + &entry); + if (rc != 0) { + pr_debug("Error: Resource entry not found for the resource type\n"); + return -EINVAL; + } + + return sprintf(page, "%ld\n", entry->limits.reserved); +} + +static ssize_t cxi_cfg_rgroup_res_type_reserved_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rgroup_res_type *rgroup_res_type; + int rc; + enum cxi_resource_type e_type; + struct cxi_resource_limits limits = {}; + + rgroup_res_type = to_cxi_cfg_rgroup_res_type(item); + + rc = kstrtoint(page, 0, &rgroup_res_type->reserved); + if (rc) + return rc; + + e_type = cxi_resource_str_to_type(rgroup_res_type->name); + + limits.max = rgroup_res_type->max; + limits.reserved = rgroup_res_type->reserved; + + rc = cxi_rgroup_add_resource(rgroup_res_type->rgroup, e_type, &limits); + if (rc) + return rc; + + return count; +} + +static ssize_t cxi_cfg_rgroup_res_type_in_use_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_res_type *rgroup_res_type; + struct cxi_resource_entry *entry; + int rc; + + rgroup_res_type = to_cxi_cfg_rgroup_res_type(item); + + rc = cxi_rgroup_get_resource_entry(rgroup_res_type->rgroup, + cxi_resource_str_to_type(rgroup_res_type->name), + &entry); + if (rc != 0) { + pr_debug("Error: Given resource type has not yet been added\n"); + return -EINVAL; + } + + return sprintf(page, "%ld\n", entry->limits.in_use); +} + +static void cxi_cfg_rgroup_res_type_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rgroup_res_type, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_rgroup_res_type_, reserved); +CONFIGFS_ATTR(cxi_cfg_rgroup_res_type_, max); +CONFIGFS_ATTR_RO(cxi_cfg_rgroup_res_type_, in_use); + +static struct configfs_attribute *cxi_cfg_rgroup_res_type_attrs[] = { + &cxi_cfg_rgroup_res_type_attr_reserved, + &cxi_cfg_rgroup_res_type_attr_max, + &cxi_cfg_rgroup_res_type_attr_in_use, + NULL +}; + +static struct configfs_item_operations cxi_cfg_rgroup_res_type_item_ops = { + .release = cxi_cfg_rgroup_res_type_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_res_type_type = { + .ct_attrs = cxi_cfg_rgroup_res_type_attrs, + .ct_item_ops = &cxi_cfg_rgroup_res_type_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rgroup_res_type *alloc_dev_rgroup_res_type(const char *name) +{ + struct cxi_cfg_rgroup_res_type *dev_dir; + + dev_dir = kzalloc(sizeof(*dev_dir), GFP_KERNEL); + if (!dev_dir) + return NULL; + + strscpy(dev_dir->name, name, sizeof(dev_dir->name)); + + config_group_init_type_name(&dev_dir->group, + dev_dir->name, + &cxi_cfg_rgroup_res_type_type); + + return dev_dir; +} + +static struct +config_group *cxi_cfg_rgroup_res_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_rgroup_res_type *res_type; + struct cxi_cfg_rgroup_res *res; + enum cxi_resource_type e_type; + + e_type = cxi_resource_str_to_type(name); + if (e_type == CXI_RESOURCE_MAX) { + pr_debug("Unknown resource name %s\n", name); + return ERR_PTR(-EINVAL); + } + + res_type = alloc_dev_rgroup_res_type(name); + if (!res_type) { + pr_debug("Failed to alloc memory for configfs resource types"); + return ERR_PTR(-ENOMEM); + } + + res = container_of(group, struct cxi_cfg_rgroup_res, group); + if (!res) { + pr_debug("No parent resource group found\n"); + kfree(res_type); + return ERR_PTR(-EINVAL); + } + + res_type->max = resource_max[e_type]; + res_type->reserved = 0; + res_type->rgroup = res->rgroup; + + return &res_type->group; +} + +static void +cxi_cfg_rgroup_res_drop_item(struct config_group *group, + struct config_item *item) +{ + struct config_group *parent_group; + int rc; + struct cxi_cfg_rgroup *rgp; + struct cxi_cfg_rgroup_res_type *res_type; + + parent_group = get_parent_config_group(group); + + rgp = container_of(parent_group, struct cxi_cfg_rgroup, group); + + res_type = container_of(to_config_group(item), + struct cxi_cfg_rgroup_res_type, + group); + + /* + * For accidental deletion of a configfs resource type directory which is + * enabled. In the future a different approach will be taken to handle + * accidental deletion by user + */ + if (cxi_rgroup_is_enabled(rgp->rgroup)) + cxi_rgroup_disable(rgp->rgroup); + + rc = cxi_rgroup_delete_resource(res_type->rgroup, + cxi_resource_str_to_type(item->ci_name)); + + pr_debug("Resource group resource deletion rc = %d\n", rc); + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_rgroup_res_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rgroup_res, group.cg_item)); +} + +static struct configfs_group_operations cxi_cfg_rgroup_res_ops = { + .make_group = cxi_cfg_rgroup_res_make_group, + .drop_item = cxi_cfg_rgroup_res_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_rgroup_res_item_ops = { + .release = cxi_cfg_rgroup_res_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_res_type = { + .ct_group_ops = &cxi_cfg_rgroup_res_ops, + .ct_item_ops = &cxi_cfg_rgroup_res_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rgroup_attr *to_cxi_cfg_rgroup_attr(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rgroup_attr, group); +} + +static ssize_t cxi_cfg_rgroup_attr_name_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_attr *rgroup_attr; + + rgroup_attr = to_cxi_cfg_rgroup_attr(item); + + return sprintf(page, "%s\n", cxi_rgroup_name(rgroup_attr->rgroup)); +} + +static ssize_t cxi_cfg_rgroup_attr_cntr_pool_id_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_attr *rgroup_attr; + + rgroup_attr = to_cxi_cfg_rgroup_attr(item); + + return sprintf(page, "%u\n", + cxi_rgroup_cntr_pool_id(rgroup_attr->rgroup)); +} + +static ssize_t cxi_cfg_rgroup_attr_system_service_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_attr *rgroup_attr; + + rgroup_attr = to_cxi_cfg_rgroup_attr(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + cxi_rgroup_system_service(rgroup_attr->rgroup) ? "true" : "false"); +} + +static ssize_t cxi_cfg_rgroup_attr_lnis_per_rgid_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_attr *rgroup_attr; + + rgroup_attr = to_cxi_cfg_rgroup_attr(item); + + return sprintf(page, "%d\n", cxi_rgroup_lnis_per_rgid(rgroup_attr->rgroup)); +} + +static void cxi_cfg_rgroup_attr_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rgroup_attr, group.cg_item)); +} + +CONFIGFS_ATTR_RO(cxi_cfg_rgroup_attr_, name); +CONFIGFS_ATTR_RO(cxi_cfg_rgroup_attr_, cntr_pool_id); +CONFIGFS_ATTR_RO(cxi_cfg_rgroup_attr_, system_service); +CONFIGFS_ATTR_RO(cxi_cfg_rgroup_attr_, lnis_per_rgid); + +static struct configfs_attribute *cxi_cfg_rgroup_attr_attrs[] = { + &cxi_cfg_rgroup_attr_attr_name, + &cxi_cfg_rgroup_attr_attr_cntr_pool_id, + &cxi_cfg_rgroup_attr_attr_system_service, + &cxi_cfg_rgroup_attr_attr_lnis_per_rgid, + NULL, +}; + +static struct configfs_item_operations cxi_cfg_rgroup_attr_item_ops = { + .release = cxi_cfg_rgroup_attr_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_attr_type = { + .ct_attrs = cxi_cfg_rgroup_attr_attrs, + .ct_item_ops = &cxi_cfg_rgroup_attr_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rgroup_state *to_cxi_cfg_rgroup_state(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rgroup_state, group); +} + +static ssize_t cxi_cfg_rgroup_state_enabled_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_state *rgroup_state; + + rgroup_state = to_cxi_cfg_rgroup_state(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + cxi_rgroup_is_enabled(rgroup_state->rgroup) ? "true" : "false"); +} + +static ssize_t cxi_cfg_rgroup_state_enabled_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rgroup_state *rgroup_state; + bool b_val; + + if (kstrtobool(page, &b_val)) + return -EINVAL; + + rgroup_state = to_cxi_cfg_rgroup_state(item); + + if (b_val) { + cxi_rgroup_enable(rgroup_state->rgroup); + } else { + cxi_rgroup_disable(rgroup_state->rgroup); + } + + return count; +} + +static ssize_t cxi_cfg_rgroup_state_refcount_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup_state *rgroup_state; + + rgroup_state = to_cxi_cfg_rgroup_state(item); + + return sprintf(page, "%d\n", cxi_rgroup_refcount(rgroup_state->rgroup)); +} + +static void cxi_cfg_rgroup_state_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rgroup_state, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_rgroup_state_, enabled); +CONFIGFS_ATTR_RO(cxi_cfg_rgroup_state_, refcount); + +static struct configfs_attribute *cxi_cfg_rgroup_state_attrs[] = { + &cxi_cfg_rgroup_state_attr_enabled, + &cxi_cfg_rgroup_state_attr_refcount, + NULL +}; + +static struct configfs_item_operations cxi_cfg_rgroup_state_item_ops = { + .release = cxi_cfg_rgroup_state_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_state_type = { + .ct_attrs = cxi_cfg_rgroup_state_attrs, + .ct_item_ops = &cxi_cfg_rgroup_state_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rgroup_ac_entry *alloc_dev_rgroup_ac_entry(const char *name) +{ + struct cxi_cfg_rgroup_ac_entry *ac_entry_dir; + + ac_entry_dir = kzalloc(sizeof(*ac_entry_dir), + GFP_KERNEL); + if (!ac_entry_dir) + return NULL; + + strscpy(ac_entry_dir->name, name, sizeof(ac_entry_dir->name)); + + config_group_init_type_name(&ac_entry_dir->group, + ac_entry_dir->name, + &cxi_cfg_rgroup_ac_entry_type); + + return ac_entry_dir; +} + +static struct cxi_cfg_rgroup_res *alloc_dev_rgroup_res(const char *name) +{ + struct cxi_cfg_rgroup_res *p_res; + + p_res = kzalloc(sizeof(*p_res), GFP_KERNEL); + if (!p_res) + return NULL; + + strscpy(p_res->name, name, sizeof(p_res->name)); + + config_group_init_type_name(&p_res->group, + p_res->name, + &cxi_cfg_rgroup_res_type); + + return p_res; +} + +static struct cxi_cfg_rgroup_state *alloc_dev_rgroup_state(const char *name) +{ + struct cxi_cfg_rgroup_state *rgp_st; + + rgp_st = kzalloc(sizeof(*rgp_st), GFP_KERNEL); + if (!rgp_st) + return NULL; + + strscpy(rgp_st->name, name, sizeof(rgp_st->name)); + + config_group_init_type_name(&rgp_st->group, + rgp_st->name, + &cxi_cfg_rgroup_state_type); + + return rgp_st; +} + +static struct cxi_cfg_rgroup_attr *alloc_dev_rgroup_attr(const char *name) +{ + struct cxi_cfg_rgroup_attr *p_attr; + + p_attr = kzalloc(sizeof(*p_attr), GFP_KERNEL); + if (!p_attr) + return NULL; + + strscpy(p_attr->name, name, sizeof(p_attr->name)); + + config_group_init_type_name(&p_attr->group, + p_attr->name, + &cxi_cfg_rgroup_attr_type); + + return p_attr; +} + +static struct +config_group *cxi_cfg_rgroup_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_rgroup *rgp = container_of(group, + struct cxi_cfg_rgroup, + group); + struct cxi_cfg_rgroup_state *rgp_state; + struct cxi_cfg_rgroup_attr *rgp_attr; + struct cxi_cfg_rgroup_ac_entry *ac_entry; + struct cxi_cfg_rgroup_res *rgp_res; + + if (strncmp(name, "resources", sizeof("resources") - 1) == 0) { + rgp_res = alloc_dev_rgroup_res("resources"); + if (!rgp_res) { + pr_debug("unable to alloc rgroup resources"); + return ERR_PTR(-EINVAL); + } + rgp_res->rgroup = rgp->rgroup; + rgp->rgp_res = rgp_res; + rgp->rgp_res->cdev = rgp->cdev; + + return &rgp->rgp_res->group; + } + + if (strncmp(name, "attr", sizeof("attr") - 1) == 0) { + rgp_attr = alloc_dev_rgroup_attr("attr"); + if (!rgp_attr) { + pr_debug("unable to alloc rgroup attr"); + return ERR_PTR(-EINVAL); + } + + rgp_attr->rgroup = rgp->rgroup; + rgp->rgp_attr = rgp_attr; + rgp->rgp_attr->cdev = rgp->cdev; + + return &rgp->rgp_attr->group; + } + + if (strncmp(name, "state", sizeof("state") - 1) == 0) { + rgp_state = alloc_dev_rgroup_state("state"); + if (!rgp_state) { + pr_debug("unable to alloc rgroup state"); + return ERR_PTR(-EINVAL); + } + rgp_state->rgroup = rgp->rgroup; + rgp->rgp_state = rgp_state; + rgp->rgp_state->cdev = rgp->cdev; + + return &rgp->rgp_state->group; + } + + if (strncmp(name, "ac-entry", sizeof("ac-entry") - 1) == 0) { + ac_entry = alloc_dev_rgroup_ac_entry("ac-entry"); + if (!ac_entry) { + pr_debug("unable to alloc rgroup ac-entry"); + return ERR_PTR(-EINVAL); + } + ac_entry->rgroup = rgp->rgroup; + rgp->ac_entry = ac_entry; + + return &rgp->ac_entry->group; + } + + return ERR_PTR(-EINVAL); +} + +static void +cxi_cfg_rgroup_drop_item(struct config_group *group, + struct config_item *item) +{ + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_rgroup_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rgroup, group.cg_item)); +} + +static struct cxi_cfg_rgroup *to_cxi_cfg_rgroup(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rgroup, group); +} + +static ssize_t cxi_cfg_rgroup_id_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rgroup *rgp = to_cxi_cfg_rgroup(item); + + return scnprintf(page, PAGE_SIZE, "%d\n", rgp->rgroup->id); +} + +CONFIGFS_ATTR_RO(cxi_cfg_rgroup_, id); + +static struct configfs_attribute *cxi_cfg_rgroup_attrs[] = { + &cxi_cfg_rgroup_attr_id, + NULL, +}; + +static struct configfs_group_operations cxi_cfg_rgroup_ops = { + .make_group = cxi_cfg_rgroup_make_group, + .drop_item = cxi_cfg_rgroup_drop_item +}; + +static struct configfs_item_operations cxi_cfg_rgroup_item_ops = { + .release = cxi_cfg_rgroup_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_type = { + .ct_group_ops = &cxi_cfg_rgroup_ops, + .ct_attrs = cxi_cfg_rgroup_attrs, + .ct_item_ops = &cxi_cfg_rgroup_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rgroup *alloc_dev_rgroup(const char *name) +{ + struct cxi_cfg_rgroup *rgp; + + rgp = kzalloc(sizeof(*rgp), GFP_KERNEL); + if (!rgp) + return NULL; + + strscpy(rgp->name, name, sizeof(rgp->name)); + + config_group_init_type_name(&rgp->group, + rgp->name, + &cxi_cfg_rgroup_type); + + return rgp; +} + +static struct +config_group *cxi_cfg_rgroup_dir_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_rgroup_dir *dev_rgp; + struct cxi_cfg_rgroup *rgp; + struct cxi_rgroup *rgroup; + struct cxi_rgroup_attr attr = { + .cntr_pool_id = 0, + .name = {'\0'}, + .system_service = false, + .lnis_per_rgid = CXI_DEFAULT_LNIS_PER_RGID + }; + + if (strlen(name) > CONFIGFS_DIR_LEN - 1) + return ERR_PTR(-EINVAL); + + dev_rgp = container_of(group, struct cxi_cfg_rgroup_dir, group); + rgp = alloc_dev_rgroup(name); + if (!rgp) { + pr_debug("Failed to alloc memory for configfs rgroup dir"); + return ERR_PTR(-ENOMEM); + } + + rgp->cdev = dev_rgp->cdev; + strscpy(attr.name, name, sizeof(attr.name)); + rgroup = cxi_dev_alloc_rgroup(dev_rgp->cdev, + &attr); + if (IS_ERR(rgroup)) { + kfree(rgp); + return ERR_PTR(PTR_ERR(rgroup)); + } + rgp->rgroup = rgroup; + + return &rgp->group; +} + +static void +cxi_cfg_rgroup_dir_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cxi_cfg_rgroup *rgp; + + rgp = container_of(item, struct cxi_cfg_rgroup, group.cg_item); + + /* + * For accidental deletion of resource group directory + * by configfs user. In the future we will employ a + * different approach to handle accidental deletion. + */ + cxi_rgroup_dec_refcount(rgp->rgroup); + + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_rgroup_dir_free(struct config_item *item) +{ + struct cxi_cfg_rgroup_dir *rgp_dir; + + rgp_dir = container_of(item, struct cxi_cfg_rgroup_dir, + group.cg_item); + kfree(rgp_dir); +} + +static struct configfs_group_operations cxi_cfg_rgroup_dir_ops = { + .make_group = cxi_cfg_rgroup_dir_make_group, + .drop_item = cxi_cfg_rgroup_dir_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_rgroup_dir_item_ops = { + .release = cxi_cfg_rgroup_dir_free, +}; + +static const struct config_item_type cxi_cfg_rgroup_dir_type = { + .ct_group_ops = &cxi_cfg_rgroup_dir_ops, + .ct_item_ops = &cxi_cfg_rgroup_dir_item_ops, + .ct_owner = THIS_MODULE, +}; + +static void cxi_cfg_group_free(struct config_item *item) +{ + struct cxi_cfg_group_item *grp_dir; + + grp_dir = container_of(item, struct cxi_cfg_group_item, + group.cg_item); + kfree(grp_dir); +} + +static struct configfs_item_operations cxi_cfg_group_item_ops = { + .release = cxi_cfg_group_free, +}; + +static const struct config_item_type cxi_cfg_group_type = { + .ct_item_ops = &cxi_cfg_group_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem cxi_cfg_group_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "cxi", + .ci_type = &cxi_cfg_group_type, + }, + }, +}; + +static struct cxi_cfg_group_item *alloc_dev_subdir(const char *name) +{ + struct cxi_cfg_group_item *dev_dir; + + dev_dir = kzalloc(sizeof(*dev_dir), GFP_KERNEL); + if (!dev_dir) + return NULL; + + strscpy(dev_dir->name, name, sizeof(dev_dir->name)); + + config_group_init_type_name(&dev_dir->group, + dev_dir->name, + &cxi_cfg_group_type); + + return dev_dir; +} + +static struct cxi_cfg_rgroup_dir *alloc_dev_attr1(const char *name) +{ + struct cxi_cfg_rgroup_dir *dev_rgp; + + dev_rgp = kzalloc(sizeof(*dev_rgp), GFP_KERNEL); + if (!dev_rgp) + return NULL; + + strscpy(dev_rgp->name, name, sizeof(dev_rgp->name)); + + config_group_init_type_name(&dev_rgp->group, + dev_rgp->name, + &cxi_cfg_rgroup_dir_type); + + return dev_rgp; +} + +static struct cxi_cfg_tx_prof_ac_entry_sub *to_cxi_cfg_tx_prof_ac_entry_sub(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_tx_prof_ac_entry_sub, group); +} + +static ssize_t cxi_cfg_tx_prof_ac_entry_sub_type_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_ac_entry_sub *tx_prof_ac_entry_sub; + int rc; + enum cxi_ac_type ac_type; + union cxi_ac_data ac_data; + + tx_prof_ac_entry_sub = to_cxi_cfg_tx_prof_ac_entry_sub(item); + + rc = cxi_tx_profile_get_ac_entry_data(tx_prof_ac_entry_sub->tx_profile, + tx_prof_ac_entry_sub->id, + &ac_type, + &ac_data); + if (rc) + return rc; + + return sprintf(page, "%d\n", ac_type); +} + +static ssize_t cxi_cfg_tx_prof_ac_entry_sub_type_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_tx_prof_ac_entry_sub *ac_entry_sub; + int rc; + enum cxi_ac_type ac_type_val; + uid_t uid; + gid_t gid; + unsigned int netns; + unsigned int ac_entry_id; + + ac_entry_sub = to_cxi_cfg_tx_prof_ac_entry_sub(item); + if (!ac_entry_sub->tx_profile) { + pr_debug("Unable to get tx profile reference\n"); + return -EINVAL; + } + + rc = kstrtouint(page, 0, (unsigned int *)&ac_type_val); + if (rc) + return rc; + + if (ac_type_val == CXI_AC_UID) + uid = ac_entry_sub->ac_data.uid; + else if (ac_type_val == CXI_AC_GID) + gid = ac_entry_sub->ac_data.gid; + else if (ac_type_val == CXI_AC_NETNS) + netns = ac_entry_sub->ac_data.netns; + else if (ac_type_val == CXI_AC_OPEN) + pr_debug("Setting type to CXI_AC_OPEN\n"); + else + return -EINVAL; + + rc = cxi_tx_profile_add_ac_entry(ac_entry_sub->tx_profile, + ac_type_val, + uid, + gid, + netns, + &ac_entry_id); + if (rc < 0) { + pr_debug("Unable to add ac entry to tx profile: err val %d\n", rc); + return rc; + } + + ac_entry_sub->id = ac_entry_id; + ac_entry_sub->ac_type = ac_type_val; + + return count; +} + +static ssize_t cxi_cfg_tx_prof_ac_entry_sub_data_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_ac_entry_sub *tx_prof_ac_entry_sub; + enum cxi_ac_type ac_type; + union cxi_ac_data ac_data; + int rc; + + tx_prof_ac_entry_sub = to_cxi_cfg_tx_prof_ac_entry_sub(item); + + rc = cxi_tx_profile_get_ac_entry_data(tx_prof_ac_entry_sub->tx_profile, + tx_prof_ac_entry_sub->id, + &ac_type, + &ac_data); + if (rc) + return rc; + + if (ac_type == CXI_AC_UID) + return sprintf(page, "%d\n", ac_data.uid); + else if (ac_type == CXI_AC_GID) + return sprintf(page, "%d\n", ac_data.gid); + else if (ac_type == CXI_AC_NETNS) + return sprintf(page, "%d\n", ac_data.netns); + else if (ac_type == CXI_AC_OPEN) + return sprintf(page, "%d\n", CXI_AC_OPEN); + + return -EINVAL; +} + +static ssize_t cxi_cfg_tx_prof_ac_entry_sub_data_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_tx_prof_ac_entry_sub *ac_entry_sub; + int rc; + unsigned int ac_data_val; + + ac_entry_sub = to_cxi_cfg_tx_prof_ac_entry_sub(item); + + rc = kstrtouint(page, 0, &ac_data_val); + if (rc) + return rc; + + if (ac_entry_sub->ac_type == CXI_AC_UID) { + ac_entry_sub->ac_data.uid = ac_data_val; + return count; + } else if (ac_entry_sub->ac_type == CXI_AC_GID) { + ac_entry_sub->ac_data.gid = ac_data_val; + return count; + } else if (ac_entry_sub->ac_type == CXI_AC_NETNS) { + ac_entry_sub->ac_data.netns = ac_data_val; + return count; + } + + return -EINVAL; +} + +static void cxi_cfg_tx_prof_ac_entry_sub_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_tx_prof_ac_entry_sub, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_tx_prof_ac_entry_sub_, type); +CONFIGFS_ATTR(cxi_cfg_tx_prof_ac_entry_sub_, data); + +static struct configfs_attribute *cxi_cfg_tx_prof_ac_entry_sub_attrs[] = { + &cxi_cfg_tx_prof_ac_entry_sub_attr_type, + &cxi_cfg_tx_prof_ac_entry_sub_attr_data, + NULL, +}; + +static struct configfs_item_operations cxi_cfg_tx_prof_ac_entry_sub_item_ops = { + .release = cxi_cfg_tx_prof_ac_entry_sub_free, +}; + +static const struct config_item_type cxi_cfg_tx_prof_ac_entry_sub_type = { + .ct_attrs = cxi_cfg_tx_prof_ac_entry_sub_attrs, + .ct_item_ops = &cxi_cfg_tx_prof_ac_entry_sub_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_tx_prof_ac_entry_sub *alloc_dev_tx_prof_ac_entry_sub(const char *name) +{ + struct cxi_cfg_tx_prof_ac_entry_sub *tx_prof_ac_entry_sub; + + tx_prof_ac_entry_sub = kzalloc(sizeof(*tx_prof_ac_entry_sub), GFP_KERNEL); + if (!tx_prof_ac_entry_sub) + return NULL; + + strscpy(tx_prof_ac_entry_sub->name, name, sizeof(tx_prof_ac_entry_sub->name)); + + config_group_init_type_name(&tx_prof_ac_entry_sub->group, + tx_prof_ac_entry_sub->name, + &cxi_cfg_tx_prof_ac_entry_sub_type); + + return tx_prof_ac_entry_sub; +} + +static struct +config_group *cxi_cfg_tx_prof_ac_entry_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_tx_prof_ac_entry *tx_prof_ac_entry; + struct cxi_cfg_tx_prof_ac_entry_sub *tx_prof_ac_entry_sub; + + if (strlen(name) > CONFIGFS_DIR_LEN - 1) + return ERR_PTR(-EINVAL); + + tx_prof_ac_entry = container_of(group, struct cxi_cfg_tx_prof_ac_entry, group); + + tx_prof_ac_entry_sub = alloc_dev_tx_prof_ac_entry_sub(name); + if (!tx_prof_ac_entry_sub) { + pr_debug("Memory allocation failed for ac entry group\n"); + return ERR_PTR(-ENOMEM); + } + + tx_prof_ac_entry_sub->ac_type = CXI_AC_UID; + tx_prof_ac_entry_sub->ac_data.uid = 0; + tx_prof_ac_entry_sub->tx_profile = tx_prof_ac_entry->tx_profile; + + return &tx_prof_ac_entry_sub->group; +} + +static void +cxi_cfg_tx_prof_ac_entry_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cxi_cfg_tx_prof_ac_entry_sub *tx_prof_ac_entry_sub; + int rc; + unsigned int ac_entry_id; + + tx_prof_ac_entry_sub = container_of(item, struct cxi_cfg_tx_prof_ac_entry_sub, group.cg_item); + + if (tx_prof_ac_entry_sub->ac_type == CXI_AC_UID) { + rc = cxi_tx_profile_get_ac_entry_id_by_data(tx_prof_ac_entry_sub->tx_profile, + CXI_AC_UID, + &tx_prof_ac_entry_sub->ac_data, + &ac_entry_id); + + pr_debug("Getting an entry info type uid rc = %d\n", rc); + } else if (tx_prof_ac_entry_sub->ac_type == CXI_AC_GID) { + rc = cxi_tx_profile_get_ac_entry_id_by_data(tx_prof_ac_entry_sub->tx_profile, + CXI_AC_GID, + &tx_prof_ac_entry_sub->ac_data, + &ac_entry_id); + + pr_debug("Getting an entry info type gid rc = %d\n", rc); + } else if (tx_prof_ac_entry_sub->ac_type == CXI_AC_NETNS) { + rc = cxi_tx_profile_get_ac_entry_id_by_data(tx_prof_ac_entry_sub->tx_profile, + CXI_AC_NETNS, + &tx_prof_ac_entry_sub->ac_data, + &ac_entry_id); + + pr_debug("Getting an entry info type netns rc = %d\n", rc); + } else { + rc = cxi_tx_profile_get_ac_entry_id_by_data(tx_prof_ac_entry_sub->tx_profile, + CXI_AC_OPEN, + NULL, + &ac_entry_id); + pr_debug("Getting an entry info type open rc = %d\n", rc); + } + + if (!rc) + cxi_tx_profile_remove_ac_entry(tx_prof_ac_entry_sub->tx_profile, + ac_entry_id); + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_tx_prof_ac_entry_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_tx_prof_ac_entry, group.cg_item)); +} + +static struct configfs_group_operations cxi_cfg_tx_prof_ac_entry_ops = { + .make_group = cxi_cfg_tx_prof_ac_entry_make_group, + .drop_item = cxi_cfg_tx_prof_ac_entry_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_tx_prof_ac_entry_item_ops = { + .release = cxi_cfg_tx_prof_ac_entry_free, +}; + +static const struct config_item_type cxi_cfg_tx_prof_ac_entry_type = { + .ct_group_ops = &cxi_cfg_tx_prof_ac_entry_ops, + .ct_item_ops = &cxi_cfg_tx_prof_ac_entry_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_tx_prof_state *to_cxi_cfg_tx_prof_state(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_tx_prof_state, group); +} + +static ssize_t cxi_cfg_tx_prof_state_enabled_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_state *tx_prof_state; + struct cxi_tx_attr tx_attr; + struct cxi_rxtx_profile_state state; + int rc; + + tx_prof_state = to_cxi_cfg_tx_prof_state(item); + + rc = cxi_tx_profile_get_info(tx_prof_state->cdev, + tx_prof_state->tx_profile, + &tx_attr, + &state); + if (rc < 0) { + pr_debug("Unable to get tx-profile info rc = %d\n", rc); + return rc; + } + + return scnprintf(page, PAGE_SIZE, "%s\n", state.enable ? "true" : "false"); +} + +static ssize_t cxi_cfg_tx_prof_state_enabled_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_tx_prof_state *tx_prof_state; + int rc; + bool b_val; + + if (kstrtobool(page, &b_val)) + return -EINVAL; + + tx_prof_state = to_cxi_cfg_tx_prof_state(item); + + if (b_val) { + rc = cxi_tx_profile_enable(tx_prof_state->cdev, + tx_prof_state->tx_profile); + if (rc) + return rc; + } else { + cxi_tx_profile_disable(tx_prof_state->cdev, + tx_prof_state->tx_profile); + } + + return count; +} + +static ssize_t cxi_cfg_tx_prof_state_refcount_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_state *tx_prof_state; + struct cxi_tx_attr tx_attr; + struct cxi_rxtx_profile_state state; + int rc; + + tx_prof_state = to_cxi_cfg_tx_prof_state(item); + + rc = cxi_tx_profile_get_info(tx_prof_state->cdev, + tx_prof_state->tx_profile, + &tx_attr, + &state); + if (rc < 0) { + pr_debug("Unable to get rx-profile info rc = %d\n", rc); + return rc; + } + + return sprintf(page, "%d\n", refcount_read(&state.refcount)); +} + +static void cxi_cfg_tx_prof_state_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_tx_prof_state, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_tx_prof_state_, enabled); +CONFIGFS_ATTR_RO(cxi_cfg_tx_prof_state_, refcount); + +static struct configfs_attribute *cxi_cfg_tx_prof_state_attrs[] = { + &cxi_cfg_tx_prof_state_attr_enabled, + &cxi_cfg_tx_prof_state_attr_refcount, + NULL +}; + +static struct configfs_item_operations cxi_cfg_tx_prof_state_item_ops = { + .release = cxi_cfg_tx_prof_state_free, +}; + +static const struct config_item_type cxi_cfg_tx_prof_state_type = { + .ct_attrs = cxi_cfg_tx_prof_state_attrs, + .ct_item_ops = &cxi_cfg_tx_prof_state_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_tx_prof_state *alloc_dev_tx_prof_state(const char *name) +{ + struct cxi_cfg_tx_prof_state *tx_prof_state; + + tx_prof_state = kzalloc(sizeof(*tx_prof_state), GFP_KERNEL); + if (!tx_prof_state) + return NULL; + + strscpy(tx_prof_state->name, name, sizeof(tx_prof_state->name)); + + config_group_init_type_name(&tx_prof_state->group, + tx_prof_state->name, + &cxi_cfg_tx_prof_state_type); + return tx_prof_state; +} + +static struct cxi_cfg_tx_prof_attr *to_cxi_cfg_tx_prof_attr(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_tx_prof_attr, group); +} + +static ssize_t cxi_cfg_tx_prof_attr_name_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + + tx_prof_attr = to_cxi_cfg_tx_prof_attr(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + tx_prof_attr->tx_profile->profile_common.vni_attr.name); +} + +static ssize_t cxi_cfg_tx_prof_attr_match_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + struct cxi_tx_attr tx_attr; + struct cxi_rxtx_profile_state state; + int rc; + + tx_prof_attr = to_cxi_cfg_tx_prof_attr(item); + + rc = cxi_tx_profile_get_info(tx_prof_attr->cdev, + tx_prof_attr->tx_profile, + &tx_attr, + &state); + if (rc < 0) + return rc; + + return scnprintf(page, PAGE_SIZE, "%d\n", tx_attr.vni_attr.match); +} + +static ssize_t cxi_cfg_tx_prof_attr_match_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + int rc; + struct cxi_tx_attr tx_attr = {}; + + tx_prof_attr = to_cxi_cfg_tx_prof_attr(item); + + rc = kstrtou16(page, 0, &tx_prof_attr->match); + if (rc) + return rc; + + tx_attr.vni_attr.match = tx_prof_attr->match; + tx_attr.vni_attr.ignore = tx_prof_attr->ignore; + + strscpy(tx_attr.vni_attr.name, + tx_prof_attr->tx_profile->profile_common.vni_attr.name, + sizeof(tx_attr.vni_attr.name)); + + rc = cxi_dev_set_tx_profile_attr(tx_prof_attr->cdev, + tx_prof_attr->tx_profile, + &tx_attr); + if (rc) + return rc; + + return count; +} + +static ssize_t cxi_cfg_tx_prof_attr_ignore_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + int rc; + struct cxi_tx_attr tx_attr; + struct cxi_rxtx_profile_state state; + + tx_prof_attr = to_cxi_cfg_tx_prof_attr(item); + + rc = cxi_tx_profile_get_info(tx_prof_attr->cdev, + tx_prof_attr->tx_profile, + &tx_attr, + &state); + if (rc < 0) { + pr_debug("Unable to get rx-profile info rc = %d\n", rc); + return rc; + } + + return scnprintf(page, PAGE_SIZE, "%d\n", tx_attr.vni_attr.ignore); +} + +static ssize_t cxi_cfg_tx_prof_attr_ignore_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + struct cxi_rxtx_profile_state state; + struct cxi_tx_attr tx_attr; + u16 ignore; + int rc; + + tx_prof_attr = to_cxi_cfg_tx_prof_attr(item); + rc = kstrtou16(page, 0, &ignore); + if (rc) + return rc; + + rc = cxi_tx_profile_get_info(tx_prof_attr->cdev, + tx_prof_attr->tx_profile, &tx_attr, &state); + if (rc < 0) + return rc; + tx_attr.vni_attr.ignore = ignore; + rc = cxi_dev_set_tx_profile_attr(tx_prof_attr->cdev, + tx_prof_attr->tx_profile, &tx_attr); + if (rc) + return rc; + tx_prof_attr->ignore = ignore; + return count; +} + +static ssize_t cxi_cfg_tx_prof_attr_exclusive_cp_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + + tx_prof_attr = to_cxi_cfg_tx_prof_attr(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + cxi_tx_profile_exclusive_cp(tx_prof_attr->tx_profile) ? "true" : "false"); +} + +static ssize_t cxi_cfg_tx_prof_attr_exclusive_cp_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + bool cp_val; + int rc; + + if (kstrtobool(page, &cp_val)) + return -EINVAL; + + tx_prof_attr = to_cxi_cfg_tx_prof_attr(item); + + rc = cxi_tx_profile_set_exclusive_cp(tx_prof_attr->tx_profile, + cp_val); + if (rc) + return rc; + + return count; +} + +static void cxi_cfg_tx_prof_attr_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_tx_prof_attr, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_tx_prof_attr_, ignore); +CONFIGFS_ATTR(cxi_cfg_tx_prof_attr_, match); +CONFIGFS_ATTR(cxi_cfg_tx_prof_attr_, exclusive_cp); +CONFIGFS_ATTR_RO(cxi_cfg_tx_prof_attr_, name); + +static struct configfs_attribute *cxi_cfg_tx_prof_attr_attrs[] = { + &cxi_cfg_tx_prof_attr_attr_name, + &cxi_cfg_tx_prof_attr_attr_match, + &cxi_cfg_tx_prof_attr_attr_ignore, + &cxi_cfg_tx_prof_attr_attr_exclusive_cp, + NULL +}; + +static struct configfs_item_operations cxi_cfg_tx_prof_attr_item_ops = { + .release = cxi_cfg_tx_prof_attr_free, +}; + +static const struct config_item_type cxi_cfg_tx_prof_attr_type = { + .ct_attrs = cxi_cfg_tx_prof_attr_attrs, + .ct_item_ops = &cxi_cfg_tx_prof_attr_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_tx_prof_tc *to_cxi_cfg_tx_prof_tc(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_tx_prof_tc, group); +} + +static void set_tc_bool_type(unsigned int tc_type, + struct cxi_cfg_tx_prof_tc *tx_prof_tc, + bool tc_bool) +{ + switch (tc_type) { + case CXI_TC_BEST_EFFORT: + tx_prof_tc->best_effort = tc_bool; + break; + case CXI_TC_DEDICATED_ACCESS: + tx_prof_tc->dedicated = tc_bool; + break; + case CXI_TC_LOW_LATENCY: + tx_prof_tc->low_latency = tc_bool; + break; + case CXI_TC_BULK_DATA: + tx_prof_tc->bulk_data = tc_bool; + break; + case CXI_TC_ETH: + tx_prof_tc->eth = tc_bool; + break; + } + + cxi_tx_profile_set_tc(tx_prof_tc->tx_profile, tc_type, tc_bool); +} + +#define cxi_cfg_tx_prof_set_tc_common(item, tc_type, page) \ + do { \ + struct cxi_cfg_tx_prof_tc *tx_prof_tc; \ + bool b_val; \ + if (kstrtobool(page, &b_val)) \ + return -EINVAL; \ + tx_prof_tc = to_cxi_cfg_tx_prof_tc((item)); \ + set_tc_bool_type((tc_type), tx_prof_tc, b_val); \ + } while (0) + +static ssize_t cxi_cfg_tx_prof_tc_best_effort_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_tc *tx_prof_tc; + + tx_prof_tc = to_cxi_cfg_tx_prof_tc(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + tx_prof_tc->best_effort ? "true" : "false"); +} + +static ssize_t cxi_cfg_tx_prof_tc_best_effort_store(struct config_item *item, + const char *page, + size_t count) +{ + cxi_cfg_tx_prof_set_tc_common(item, CXI_TC_BEST_EFFORT, page); + + return count; +} + +static ssize_t cxi_cfg_tx_prof_tc_dedicated_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_tc *tx_prof_tc; + + tx_prof_tc = to_cxi_cfg_tx_prof_tc(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + tx_prof_tc->dedicated ? "true" : "false"); +} + +static ssize_t cxi_cfg_tx_prof_tc_dedicated_store(struct config_item *item, + const char *page, + size_t count) +{ + cxi_cfg_tx_prof_set_tc_common(item, CXI_TC_DEDICATED_ACCESS, page); + + return count; +} + +static ssize_t cxi_cfg_tx_prof_tc_low_latency_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_tc *tx_prof_tc; + + tx_prof_tc = to_cxi_cfg_tx_prof_tc(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + tx_prof_tc->low_latency ? "true" : "false"); +} + +static ssize_t cxi_cfg_tx_prof_tc_low_latency_store(struct config_item *item, + const char *page, + size_t count) +{ + cxi_cfg_tx_prof_set_tc_common(item, CXI_TC_LOW_LATENCY, page); + + return count; +} + +static ssize_t cxi_cfg_tx_prof_tc_bulk_data_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_tc *tx_prof_tc; + + tx_prof_tc = to_cxi_cfg_tx_prof_tc(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + tx_prof_tc->bulk_data ? "true" : "false"); +} + +static ssize_t cxi_cfg_tx_prof_tc_bulk_data_store(struct config_item *item, + const char *page, + size_t count) +{ + cxi_cfg_tx_prof_set_tc_common(item, CXI_TC_BULK_DATA, page); + + return count; +} + +static ssize_t cxi_cfg_tx_prof_tc_eth_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof_tc *tx_prof_tc; + + tx_prof_tc = to_cxi_cfg_tx_prof_tc(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + tx_prof_tc->eth ? "true" : "false"); +} + +static void cxi_cfg_tx_prof_tc_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_tx_prof_tc, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_tx_prof_tc_, best_effort); +CONFIGFS_ATTR(cxi_cfg_tx_prof_tc_, dedicated); +CONFIGFS_ATTR(cxi_cfg_tx_prof_tc_, low_latency); +CONFIGFS_ATTR(cxi_cfg_tx_prof_tc_, bulk_data); +CONFIGFS_ATTR_RO(cxi_cfg_tx_prof_tc_, eth); + +static struct configfs_attribute *cxi_cfg_tx_prof_tc_attrs[] = { + &cxi_cfg_tx_prof_tc_attr_best_effort, + &cxi_cfg_tx_prof_tc_attr_dedicated, + &cxi_cfg_tx_prof_tc_attr_low_latency, + &cxi_cfg_tx_prof_tc_attr_bulk_data, + &cxi_cfg_tx_prof_tc_attr_eth, + NULL +}; + +static struct configfs_item_operations cxi_cfg_tx_prof_tc_item_ops = { + .release = cxi_cfg_tx_prof_tc_free, +}; + +static const struct config_item_type cxi_cfg_tx_prof_tc_type = { + .ct_attrs = cxi_cfg_tx_prof_tc_attrs, + .ct_item_ops = &cxi_cfg_tx_prof_tc_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_tx_prof_tc *alloc_dev_tx_prof_tc(const char *name) +{ + struct cxi_cfg_tx_prof_tc *tx_prof_tc; + + tx_prof_tc = kzalloc(sizeof(*tx_prof_tc), GFP_KERNEL); + if (!tx_prof_tc) + return NULL; + + strscpy(tx_prof_tc->name, name, sizeof(tx_prof_tc->name)); + + config_group_init_type_name(&tx_prof_tc->group, + tx_prof_tc->name, + &cxi_cfg_tx_prof_tc_type); + + return tx_prof_tc; +} + +static struct cxi_cfg_tx_prof_attr *alloc_dev_tx_prof_attr(const char *name) +{ + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + + tx_prof_attr = kzalloc(sizeof(*tx_prof_attr), GFP_KERNEL); + if (!tx_prof_attr) + return NULL; + + strscpy(tx_prof_attr->name, name, sizeof(tx_prof_attr->name)); + + config_group_init_type_name(&tx_prof_attr->group, + tx_prof_attr->name, + &cxi_cfg_tx_prof_attr_type); + + return tx_prof_attr; +} + +static struct cxi_cfg_tx_prof_ac_entry *alloc_dev_tx_prof_ac_entry(const char *name) +{ + struct cxi_cfg_tx_prof_ac_entry *tx_prof_ac_entry; + + tx_prof_ac_entry = kzalloc(sizeof(*tx_prof_ac_entry), + GFP_KERNEL); + if (!tx_prof_ac_entry) + return NULL; + + strscpy(tx_prof_ac_entry->name, + name, + sizeof(tx_prof_ac_entry->name)); + + config_group_init_type_name(&tx_prof_ac_entry->group, + tx_prof_ac_entry->name, + &cxi_cfg_tx_prof_ac_entry_type); + + return tx_prof_ac_entry; +} + +static struct +config_group *cxi_cfg_tx_prof_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_tx_prof *tx_prof; + struct cxi_cfg_tx_prof_state *tx_prof_state; + struct cxi_cfg_tx_prof_attr *tx_prof_attr; + struct cxi_cfg_tx_prof_ac_entry *tx_prof_ac_entry; + struct cxi_cfg_tx_prof_tc *tx_prof_tc; + + tx_prof = container_of(group, + struct cxi_cfg_tx_prof, + group); + + if (strncmp(name, "attr", sizeof("attr") - 1) == 0) { + tx_prof_attr = alloc_dev_tx_prof_attr("attr"); + if (!tx_prof_attr) + return ERR_PTR(-ENOMEM); + tx_prof->tx_prof_attr = tx_prof_attr; + tx_prof->tx_prof_attr->cdev = tx_prof->cdev; + tx_prof_attr->tx_profile = tx_prof->tx_profile; + tx_prof->tx_prof_attr->cdev = tx_prof->cdev; + + return &tx_prof->tx_prof_attr->group; + } + + if (strncmp(name, "state", sizeof("state") - 1) == 0) { + tx_prof_state = alloc_dev_tx_prof_state("state"); + if (!tx_prof_state) + return ERR_PTR(-ENOMEM); + tx_prof->tx_prof_state = tx_prof_state; + tx_prof_state->tx_profile = tx_prof->tx_profile; + tx_prof->tx_prof_state->cdev = tx_prof->cdev; + + return &tx_prof->tx_prof_state->group; + } + + if (strncmp(name, "ac-entry", sizeof("ac-entry") - 1) == 0) { + tx_prof_ac_entry = alloc_dev_tx_prof_ac_entry("ac-entry"); + if (!tx_prof_ac_entry) + return ERR_PTR(-ENOMEM); + tx_prof->tx_prof_ac_entry = tx_prof_ac_entry; + tx_prof_ac_entry->tx_profile = tx_prof->tx_profile; + tx_prof_ac_entry->cdev = tx_prof->cdev; + + return &tx_prof->tx_prof_ac_entry->group; + } + + if (strncmp(name, "tc", sizeof("tc") - 1) == 0) { + tx_prof_tc = alloc_dev_tx_prof_tc("tc"); + if (!tx_prof_tc) + return ERR_PTR(-ENOMEM); + tx_prof->tx_prof_tc = tx_prof_tc; + tx_prof_tc->tx_profile = tx_prof->tx_profile; + tx_prof_tc->cdev = tx_prof->cdev; + + return &tx_prof->tx_prof_tc->group; + } + + return ERR_PTR(-EINVAL); +} + +static void +cxi_cfg_tx_prof_drop_item(struct config_group *group, + struct config_item *item) +{ + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_tx_prof_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_tx_prof, group.cg_item)); +} + +static struct cxi_cfg_tx_prof *to_cxi_cfg_tx_prof(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_tx_prof, group); +} + +static ssize_t cxi_cfg_tx_prof_id_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_tx_prof *tx_prof = to_cxi_cfg_tx_prof(item); + + return scnprintf(page, PAGE_SIZE, "%d\n", tx_prof->id); +} + +CONFIGFS_ATTR_RO(cxi_cfg_tx_prof_, id); + +static struct configfs_attribute *cxi_cfg_tx_prof_attrs[] = { + &cxi_cfg_tx_prof_attr_id, + NULL, +}; + +static struct configfs_group_operations cxi_cfg_tx_prof_ops = { + .make_group = cxi_cfg_tx_prof_make_group, + .drop_item = cxi_cfg_tx_prof_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_tx_prof_item_ops = { + .release = cxi_cfg_tx_prof_free, +}; + +static const struct config_item_type cxi_cfg_tx_prof_type = { + .ct_group_ops = &cxi_cfg_tx_prof_ops, + .ct_attrs = cxi_cfg_tx_prof_attrs, + .ct_item_ops = &cxi_cfg_tx_prof_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_tx_prof *alloc_dev_tx_prof(const char *name) +{ + struct cxi_cfg_tx_prof *dev_tx_prof; + + dev_tx_prof = kzalloc(sizeof(struct cxi_cfg_tx_prof), GFP_KERNEL); + if (!dev_tx_prof) + return NULL; + + strscpy(dev_tx_prof->name, name, sizeof(dev_tx_prof->name)); + + config_group_init_type_name(&dev_tx_prof->group, + dev_tx_prof->name, + &cxi_cfg_tx_prof_type); + + return dev_tx_prof; +} + +static struct +config_group *cxi_cfg_tx_prof_dir_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_tx_prof_dir *tx_prof_dir; + struct cxi_cfg_tx_prof *tx_prof; + struct cxi_tx_profile *tx_prof_ptr; + struct cxi_tx_attr tx_attr = {}; + + if (strlen(name) > CONFIGFS_DIR_LEN - 1) + return ERR_PTR(-EINVAL); + + tx_prof_dir = container_of(group, struct cxi_cfg_tx_prof_dir, group); + tx_prof = alloc_dev_tx_prof(name); + if (!tx_prof) { + pr_debug("Failed to alloc memory\n"); + return ERR_PTR(-ENOMEM); + } + + tx_prof->cdev = tx_prof_dir->cdev; + strscpy(tx_attr.vni_attr.name, name, sizeof(tx_attr.vni_attr.name)); + + tx_prof_ptr = cxi_dev_alloc_tx_profile(tx_prof->cdev, + &tx_attr); + if (IS_ERR(tx_prof_ptr)) { + kfree(tx_prof); + pr_debug("Failed to alloc rx profile\n"); + return ERR_PTR(PTR_ERR(tx_prof_ptr)); + } + + tx_prof->tx_profile = tx_prof_ptr; + tx_prof->id = tx_prof_ptr->profile_common.id; + + return &tx_prof->group; +} + +static void +cxi_cfg_tx_prof_dir_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cxi_cfg_tx_prof *tx_prof; + + tx_prof = container_of(item, struct cxi_cfg_tx_prof, group.cg_item); + + /* + * For accidental deletion of resource group directory + * by configfs user. In the future we will employ a + * different approach to handle accidental deletion. + */ + cxi_tx_profile_dec_refcount(tx_prof->cdev, + tx_prof->tx_profile, + true); + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_tx_prof_dir_free(struct config_item *item) +{ + struct cxi_cfg_tx_prof_dir *tx_prof_dir; + + tx_prof_dir = container_of(item, struct cxi_cfg_tx_prof_dir, + group.cg_item); + kfree(tx_prof_dir); +} + +static struct configfs_group_operations cxi_cfg_tx_prof_dir_ops = { + .make_group = cxi_cfg_tx_prof_dir_make_group, + .drop_item = cxi_cfg_tx_prof_dir_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_tx_prof_dir_item_ops = { + .release = cxi_cfg_tx_prof_dir_free, +}; + +static const struct config_item_type cxi_cfg_tx_prof_dir_type = { + .ct_group_ops = &cxi_cfg_tx_prof_dir_ops, + .ct_item_ops = &cxi_cfg_tx_prof_dir_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_tx_prof_dir *alloc_dev_tx_prof_dir(const char *name) +{ + struct cxi_cfg_tx_prof_dir *dev_tx_prof_dir; + + dev_tx_prof_dir = kzalloc(sizeof(*dev_tx_prof_dir), GFP_KERNEL); + if (!dev_tx_prof_dir) + return NULL; + + strscpy(dev_tx_prof_dir->name, name, sizeof(dev_tx_prof_dir->name)); + + config_group_init_type_name(&dev_tx_prof_dir->group, + dev_tx_prof_dir->name, + &cxi_cfg_tx_prof_dir_type); + + return dev_tx_prof_dir; +} + +static struct cxi_cfg_rx_prof_ac_entry_sub *to_cxi_cfg_rx_prof_ac_entry_sub(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rx_prof_ac_entry_sub, group); +} + +static ssize_t cxi_cfg_rx_prof_ac_entry_sub_type_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof_ac_entry_sub *rx_prof_ac_entry_sub; + enum cxi_ac_type ac_type; + union cxi_ac_data ac_data; + int rc; + + rx_prof_ac_entry_sub = to_cxi_cfg_rx_prof_ac_entry_sub(item); + + rc = cxi_rx_profile_get_ac_entry_data(rx_prof_ac_entry_sub->rx_profile, + rx_prof_ac_entry_sub->id, + &ac_type, + &ac_data); + if (rc) + return rc; + + return sprintf(page, "%d\n", ac_type); +} + +static ssize_t cxi_cfg_rx_prof_ac_entry_sub_type_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rx_prof_ac_entry_sub *ac_entry_sub; + int rc; + enum cxi_ac_type ac_type_val; + uid_t uid; + gid_t gid; + unsigned int netns; + unsigned int ac_entry_id; + + ac_entry_sub = to_cxi_cfg_rx_prof_ac_entry_sub(item); + if (!ac_entry_sub->rx_profile) { + pr_debug("Unable to get rx profile reference\n"); + return -EINVAL; + } + + rc = kstrtouint(page, 0, (unsigned int *)&ac_type_val); + if (rc) + return rc; + + if (ac_type_val == CXI_AC_UID) + uid = ac_entry_sub->ac_data.uid; + else if (ac_type_val == CXI_AC_GID) + gid = ac_entry_sub->ac_data.gid; + else if (ac_type_val == CXI_AC_NETNS) + netns = ac_entry_sub->ac_data.netns; + else if (ac_type_val == CXI_AC_OPEN) + pr_debug("Setting type to CXI_AC_OPEN\n"); + else + return -EINVAL; + + rc = cxi_rx_profile_add_ac_entry(ac_entry_sub->rx_profile, + ac_type_val, + uid, + gid, + netns, + &ac_entry_id); + if (rc < 0) { + pr_debug("Unable to add ac entry to rx profile: err val %d\n", rc); + return rc; + } + + ac_entry_sub->id = ac_entry_id; + ac_entry_sub->ac_type = ac_type_val; + + return count; +} + +static ssize_t cxi_cfg_rx_prof_ac_entry_sub_data_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof_ac_entry_sub *rx_prof_ac_entry_sub; + int rc; + enum cxi_ac_type ac_type; + union cxi_ac_data ac_data; + + rx_prof_ac_entry_sub = to_cxi_cfg_rx_prof_ac_entry_sub(item); + + rc = cxi_rx_profile_get_ac_entry_data(rx_prof_ac_entry_sub->rx_profile, + rx_prof_ac_entry_sub->id, + &ac_type, + &ac_data); + if (rc) + return rc; + + if (ac_type == CXI_AC_UID) + return sprintf(page, "%d\n", ac_data.uid); + else if (ac_type == CXI_AC_GID) + return sprintf(page, "%d\n", ac_data.gid); + else if (ac_type == CXI_AC_NETNS) + return sprintf(page, "%d\n", ac_data.netns); + else if (ac_type == CXI_AC_OPEN) + return sprintf(page, "%d\n", CXI_AC_OPEN); + + return -EINVAL; +} + +static ssize_t cxi_cfg_rx_prof_ac_entry_sub_data_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rx_prof_ac_entry_sub *ac_entry_sub; + int rc; + unsigned int ac_data_val; + + ac_entry_sub = to_cxi_cfg_rx_prof_ac_entry_sub(item); + + rc = kstrtouint(page, 0, &ac_data_val); + if (rc) + return rc; + + if (ac_entry_sub->ac_type == CXI_AC_UID) { + ac_entry_sub->ac_data.uid = ac_data_val; + return count; + } else if (ac_entry_sub->ac_type == CXI_AC_GID) { + ac_entry_sub->ac_data.gid = ac_data_val; + return count; + } else if (ac_entry_sub->ac_type == CXI_AC_NETNS) { + ac_entry_sub->ac_data.netns = ac_data_val; + return count; + } + + return -EINVAL; +} + +static void cxi_cfg_rx_prof_ac_entry_sub_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rx_prof_ac_entry_sub, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_rx_prof_ac_entry_sub_, type); +CONFIGFS_ATTR(cxi_cfg_rx_prof_ac_entry_sub_, data); + +static struct configfs_attribute *cxi_cfg_rx_prof_ac_entry_sub_attrs[] = { + &cxi_cfg_rx_prof_ac_entry_sub_attr_type, + &cxi_cfg_rx_prof_ac_entry_sub_attr_data, + NULL, +}; + +static struct configfs_item_operations cxi_cfg_rx_prof_ac_entry_sub_item_ops = { + .release = cxi_cfg_rx_prof_ac_entry_sub_free, +}; + +static const struct config_item_type cxi_cfg_rx_prof_ac_entry_sub_type = { + .ct_attrs = cxi_cfg_rx_prof_ac_entry_sub_attrs, + .ct_item_ops = &cxi_cfg_rx_prof_ac_entry_sub_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rx_prof_ac_entry_sub *alloc_dev_rx_prof_ac_entry_sub(const char *name) +{ + struct cxi_cfg_rx_prof_ac_entry_sub *rx_prof_ac_entry_sub; + + rx_prof_ac_entry_sub = kzalloc(sizeof(*rx_prof_ac_entry_sub), GFP_KERNEL); + if (!rx_prof_ac_entry_sub) + return NULL; + + strscpy(rx_prof_ac_entry_sub->name, name, sizeof(rx_prof_ac_entry_sub->name)); + + config_group_init_type_name(&rx_prof_ac_entry_sub->group, + rx_prof_ac_entry_sub->name, + &cxi_cfg_rx_prof_ac_entry_sub_type); + + return rx_prof_ac_entry_sub; +} + +static struct +config_group *cxi_cfg_rx_prof_ac_entry_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_rx_prof_ac_entry *rx_prof_ac_entry; + struct cxi_cfg_rx_prof_ac_entry_sub *rx_prof_ac_entry_sub; + + if (strlen(name) > CONFIGFS_DIR_LEN - 1) + return ERR_PTR(-EINVAL); + + rx_prof_ac_entry_sub = alloc_dev_rx_prof_ac_entry_sub(name); + if (!rx_prof_ac_entry_sub) { + pr_debug("Memory allocation failed for ac entry group\n"); + return ERR_PTR(-ENOMEM); + } + + rx_prof_ac_entry_sub->ac_type = CXI_AC_UID; + rx_prof_ac_entry_sub->ac_data.uid = 0; + + rx_prof_ac_entry = container_of(group, struct cxi_cfg_rx_prof_ac_entry, group); + rx_prof_ac_entry_sub->rx_profile = rx_prof_ac_entry->rx_profile; + + return &rx_prof_ac_entry_sub->group; +} + +static void +cxi_cfg_rx_prof_ac_entry_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cxi_cfg_rx_prof_ac_entry_sub *rx_prof_ac_entry_sub; + + rx_prof_ac_entry_sub = container_of(item, struct cxi_cfg_rx_prof_ac_entry_sub, group.cg_item); + cxi_rx_profile_remove_ac_entry(rx_prof_ac_entry_sub->rx_profile, + rx_prof_ac_entry_sub->id); + + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_rx_prof_ac_entry_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rx_prof_ac_entry, group.cg_item)); +} + +static struct configfs_group_operations cxi_cfg_rx_prof_ac_entry_ops = { + .make_group = cxi_cfg_rx_prof_ac_entry_make_group, + .drop_item = cxi_cfg_rx_prof_ac_entry_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_rx_prof_ac_entry_item_ops = { + .release = cxi_cfg_rx_prof_ac_entry_free, +}; + +static const struct config_item_type cxi_cfg_rx_prof_ac_entry_type = { + .ct_group_ops = &cxi_cfg_rx_prof_ac_entry_ops, + .ct_item_ops = &cxi_cfg_rx_prof_ac_entry_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rx_prof_state *to_cxi_cfg_rx_prof_state(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rx_prof_state, group); +} + +static ssize_t cxi_cfg_rx_prof_state_enabled_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof_state *rx_prof_state; + struct cxi_rx_attr rx_attr; + struct cxi_rxtx_profile_state state; + int rc; + + rx_prof_state = to_cxi_cfg_rx_prof_state(item); + + rc = cxi_rx_profile_get_info(rx_prof_state->cdev, + rx_prof_state->rx_profile, + &rx_attr, + &state); + if (rc < 0) { + pr_debug("Unable to get rx-profile info rc = %d\n", rc); + return rc; + } + + return scnprintf(page, PAGE_SIZE, "%s\n", state.enable ? "true" : "false"); +} + +static ssize_t cxi_cfg_rx_prof_state_enabled_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rx_prof_state *rx_prof_state; + int rc; + bool b_val; + + if (kstrtobool(page, &b_val)) + return -EINVAL; + + rx_prof_state = to_cxi_cfg_rx_prof_state(item); + + if (b_val) { + rc = cxi_rx_profile_enable(rx_prof_state->cdev, + rx_prof_state->rx_profile); + if (rc) + return rc; + } else { + cxi_rx_profile_disable(rx_prof_state->cdev, + rx_prof_state->rx_profile); + } + + return count; +} + +static ssize_t cxi_cfg_rx_prof_state_refcount_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof_state *rx_prof_state; + struct cxi_rx_attr rx_attr; + struct cxi_rxtx_profile_state state; + int rc; + + rx_prof_state = to_cxi_cfg_rx_prof_state(item); + + rc = cxi_rx_profile_get_info(rx_prof_state->cdev, + rx_prof_state->rx_profile, + &rx_attr, + &state); + if (rc < 0) { + pr_debug("Unable to get rx-profile info rc = %d\n", rc); + return rc; + } + + return sprintf(page, "%d\n", refcount_read(&state.refcount)); +} + +static void cxi_cfg_rx_prof_state_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rx_prof_state, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_rx_prof_state_, enabled); +CONFIGFS_ATTR_RO(cxi_cfg_rx_prof_state_, refcount); + +static struct configfs_attribute *cxi_cfg_rx_prof_state_attrs[] = { + &cxi_cfg_rx_prof_state_attr_enabled, + &cxi_cfg_rx_prof_state_attr_refcount, + NULL +}; + +static struct configfs_item_operations cxi_cfg_rx_prof_state_item_ops = { + .release = cxi_cfg_rx_prof_state_free, +}; + +static const struct config_item_type cxi_cfg_rx_prof_state_type = { + .ct_attrs = cxi_cfg_rx_prof_state_attrs, + .ct_item_ops = &cxi_cfg_rx_prof_state_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rx_prof_state *alloc_dev_rx_prof_state(const char *name) +{ + struct cxi_cfg_rx_prof_state *rx_prof_state; + + rx_prof_state = kzalloc(sizeof(*rx_prof_state), GFP_KERNEL); + if (!rx_prof_state) + return NULL; + + strscpy(rx_prof_state->name, name, sizeof(rx_prof_state->name)); + + config_group_init_type_name(&rx_prof_state->group, + rx_prof_state->name, + &cxi_cfg_rx_prof_state_type); + + return rx_prof_state; +} + +static struct cxi_cfg_rx_prof_attr *to_cxi_cfg_rx_prof_attr(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rx_prof_attr, group); +} + +static ssize_t cxi_cfg_rx_prof_attr_name_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof_attr *rx_prof_attr; + + rx_prof_attr = to_cxi_cfg_rx_prof_attr(item); + + return scnprintf(page, PAGE_SIZE, "%s\n", + rx_prof_attr->rx_profile->profile_common.vni_attr.name); +} + +static ssize_t cxi_cfg_rx_prof_attr_match_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof_attr *rx_prof_attr; + struct cxi_rx_attr rx_attr; + struct cxi_rxtx_profile_state state; + int rc; + + rx_prof_attr = to_cxi_cfg_rx_prof_attr(item); + + rc = cxi_rx_profile_get_info(rx_prof_attr->cdev, + rx_prof_attr->rx_profile, + &rx_attr, + &state); + if (rc < 0) + return rc; + + return scnprintf(page, PAGE_SIZE, "%d\n", rx_attr.vni_attr.match); +} + +static ssize_t cxi_cfg_rx_prof_attr_match_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rx_prof_attr *rx_prof_attr; + int rc; + struct cxi_rx_attr rx_attr = {}; + + rx_prof_attr = to_cxi_cfg_rx_prof_attr(item); + rc = kstrtou16(page, 0, &rx_prof_attr->match); + if (rc) + return rc; + + rx_attr.vni_attr.match = rx_prof_attr->match; + rx_attr.vni_attr.ignore = rx_prof_attr->ignore; + + strscpy(rx_attr.vni_attr.name, + rx_prof_attr->rx_profile->profile_common.vni_attr.name, + sizeof(rx_attr.vni_attr.name)); + + rc = cxi_dev_set_rx_profile_attr(rx_prof_attr->cdev, + rx_prof_attr->rx_profile, + &rx_attr); + if (rc) + return rc; + + return count; +} + +static ssize_t cxi_cfg_rx_prof_attr_ignore_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof_attr *rx_prof_attr; + int rc; + struct cxi_rx_attr rx_attr; + struct cxi_rxtx_profile_state state; + + rx_prof_attr = to_cxi_cfg_rx_prof_attr(item); + + rc = cxi_rx_profile_get_info(rx_prof_attr->cdev, + rx_prof_attr->rx_profile, + &rx_attr, + &state); + if (rc < 0) { + pr_err("Unable to get rx-profile info rc = %d\n", rc); + return rc; + } + + return scnprintf(page, PAGE_SIZE, "%d\n", rx_attr.vni_attr.ignore); +} + +static ssize_t cxi_cfg_rx_prof_attr_ignore_store(struct config_item *item, + const char *page, + size_t count) +{ + struct cxi_cfg_rx_prof_attr *rx_prof_attr; + struct cxi_rxtx_profile_state state; + struct cxi_rx_attr rx_attr; + u16 ignore; + int rc; + + rx_prof_attr = to_cxi_cfg_rx_prof_attr(item); + rc = kstrtou16(page, 0, &ignore); + if (rc) + return rc; + + rc = cxi_rx_profile_get_info(rx_prof_attr->cdev, + rx_prof_attr->rx_profile, &rx_attr, &state); + if (rc < 0) + return rc; + rx_attr.vni_attr.ignore = ignore; + rc = cxi_dev_set_rx_profile_attr(rx_prof_attr->cdev, + rx_prof_attr->rx_profile, &rx_attr); + if (rc) + return rc; + rx_prof_attr->ignore = ignore; + return count; +} + +static void cxi_cfg_rx_prof_attr_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rx_prof_attr, group.cg_item)); +} + +CONFIGFS_ATTR(cxi_cfg_rx_prof_attr_, ignore); +CONFIGFS_ATTR(cxi_cfg_rx_prof_attr_, match); +CONFIGFS_ATTR_RO(cxi_cfg_rx_prof_attr_, name); + +static struct configfs_attribute *cxi_cfg_rx_prof_attr_attrs[] = { + &cxi_cfg_rx_prof_attr_attr_name, + &cxi_cfg_rx_prof_attr_attr_match, + &cxi_cfg_rx_prof_attr_attr_ignore, + NULL +}; + +static struct configfs_item_operations cxi_cfg_rx_prof_attr_item_ops = { + .release = cxi_cfg_rx_prof_attr_free, +}; + +static const struct config_item_type cxi_cfg_rx_prof_attr_type = { + .ct_attrs = cxi_cfg_rx_prof_attr_attrs, + .ct_item_ops = &cxi_cfg_rx_prof_attr_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rx_prof_attr *alloc_dev_rx_prof_attr(const char *name) +{ + struct cxi_cfg_rx_prof_attr *rx_prof_attr; + + rx_prof_attr = kzalloc(sizeof(*rx_prof_attr), GFP_KERNEL); + if (!rx_prof_attr) + return NULL; + + strscpy(rx_prof_attr->name, name, sizeof(rx_prof_attr->name)); + + config_group_init_type_name(&rx_prof_attr->group, + rx_prof_attr->name, + &cxi_cfg_rx_prof_attr_type); + + return rx_prof_attr; +} + +static struct cxi_cfg_rx_prof_ac_entry *alloc_dev_rx_prof_ac_entry(const char *name) +{ + struct cxi_cfg_rx_prof_ac_entry *rx_prof_ac_entry; + + rx_prof_ac_entry = kzalloc(sizeof(*rx_prof_ac_entry), + GFP_KERNEL); + if (!rx_prof_ac_entry) + return NULL; + + strscpy(rx_prof_ac_entry->name, + name, + sizeof(rx_prof_ac_entry->name)); + + config_group_init_type_name(&rx_prof_ac_entry->group, + rx_prof_ac_entry->name, + &cxi_cfg_rx_prof_ac_entry_type); + + return rx_prof_ac_entry; +} + +static struct +config_group *cxi_cfg_rx_prof_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_rx_prof *rx_prof; + struct cxi_cfg_rx_prof_state *rx_prof_state; + struct cxi_cfg_rx_prof_attr *rx_prof_attr; + struct cxi_cfg_rx_prof_ac_entry *rx_prof_ac_entry; + + rx_prof = container_of(group, struct cxi_cfg_rx_prof, group); + + if (strncmp(name, "attr", sizeof("attr") - 1) == 0) { + rx_prof_attr = alloc_dev_rx_prof_attr("attr"); + if (!rx_prof_attr) + return ERR_PTR(-ENOMEM); + + rx_prof->rx_prof_attr = rx_prof_attr; + rx_prof->rx_prof_attr->cdev = rx_prof->cdev; + rx_prof_attr->rx_profile = rx_prof->rx_profile; + rx_prof->rx_prof_attr->cdev = rx_prof->cdev; + + return &rx_prof->rx_prof_attr->group; + } + + if (strncmp(name, "state", sizeof("state") - 1) == 0) { + rx_prof_state = alloc_dev_rx_prof_state("state"); + if (!rx_prof_state) + return ERR_PTR(-ENOMEM); + + rx_prof->rx_prof_state = rx_prof_state; + rx_prof_state->rx_profile = rx_prof->rx_profile; + rx_prof->rx_prof_state->cdev = rx_prof->cdev; + + return &rx_prof->rx_prof_state->group; + } + + if (strncmp(name, "ac-entry", sizeof("ac-entry") - 1) == 0) { + rx_prof_ac_entry = alloc_dev_rx_prof_ac_entry("ac-entry"); + if (!rx_prof_ac_entry) + return ERR_PTR(-ENOMEM); + + rx_prof->rx_prof_ac_entry = rx_prof_ac_entry; + rx_prof_ac_entry->rx_profile = rx_prof->rx_profile; + rx_prof_ac_entry->cdev = rx_prof->cdev; + + return &rx_prof->rx_prof_ac_entry->group; + } + + return ERR_PTR(-EINVAL); +} + +static void +cxi_cfg_rx_prof_drop_item(struct config_group *group, + struct config_item *item) +{ + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_rx_prof_free(struct config_item *item) +{ + kfree(container_of(item, struct cxi_cfg_rx_prof, group.cg_item)); +} + +static struct cxi_cfg_rx_prof *to_cxi_cfg_rx_prof(struct config_item *item) +{ + return container_of(to_config_group(item), struct cxi_cfg_rx_prof, group); +} + +static ssize_t cxi_cfg_rx_prof_id_show(struct config_item *item, + char *page) +{ + struct cxi_cfg_rx_prof *rx_prof = to_cxi_cfg_rx_prof(item); + + return scnprintf(page, PAGE_SIZE, "%d\n", rx_prof->id); +} + +CONFIGFS_ATTR_RO(cxi_cfg_rx_prof_, id); + +static struct configfs_attribute *cxi_cfg_rx_prof_attrs[] = { + &cxi_cfg_rx_prof_attr_id, + NULL, +}; + +static struct configfs_group_operations cxi_cfg_rx_prof_ops = { + .make_group = cxi_cfg_rx_prof_make_group, + .drop_item = cxi_cfg_rx_prof_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_rx_prof_item_ops = { + .release = cxi_cfg_rx_prof_free, +}; + +static const struct config_item_type cxi_cfg_rx_prof_type = { + .ct_group_ops = &cxi_cfg_rx_prof_ops, + .ct_attrs = cxi_cfg_rx_prof_attrs, + .ct_item_ops = &cxi_cfg_rx_prof_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rx_prof *alloc_dev_rx_prof(const char *name) +{ + struct cxi_cfg_rx_prof *dev_rx_prof; + + dev_rx_prof = kzalloc(sizeof(struct cxi_cfg_rx_prof), GFP_KERNEL); + if (!dev_rx_prof) + return NULL; + + strscpy(dev_rx_prof->name, name, sizeof(dev_rx_prof->name)); + + config_group_init_type_name(&dev_rx_prof->group, + dev_rx_prof->name, + &cxi_cfg_rx_prof_type); + + return dev_rx_prof; +} + +static struct +config_group *cxi_cfg_rx_prof_dir_make_group(struct config_group *group, + const char *name) +{ + struct cxi_cfg_rx_prof_dir *rx_prof_dir; + struct cxi_cfg_rx_prof *rx_prof; + struct cxi_rx_profile *rx_prof_ptr; + struct cxi_rx_attr rx_attr = {}; + + if (strlen(name) > CONFIGFS_DIR_LEN - 1) + return ERR_PTR(-EINVAL); + + rx_prof_dir = container_of(group, struct cxi_cfg_rx_prof_dir, group); + rx_prof = alloc_dev_rx_prof(name); + if (!rx_prof) { + pr_debug("Failed to alloc memory\n"); + return ERR_PTR(-ENOMEM); + } + + rx_prof->cdev = rx_prof_dir->cdev; + + strscpy(rx_attr.vni_attr.name, name, sizeof(rx_attr.vni_attr.name)); + + rx_prof_ptr = cxi_dev_alloc_rx_profile(rx_prof->cdev, + &rx_attr); + if (IS_ERR(rx_prof_ptr)) { + kfree(rx_prof); + pr_debug("Failed to alloc rx profile\n"); + return ERR_PTR(PTR_ERR(rx_prof_ptr)); + } + + rx_prof->rx_profile = rx_prof_ptr; + rx_prof->id = rx_prof_ptr->profile_common.id; + + return &rx_prof->group; +} + +static void +cxi_cfg_rx_prof_dir_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cxi_cfg_rx_prof *rx_prof; + + rx_prof = container_of(item, struct cxi_cfg_rx_prof, group.cg_item); + + /* + * For accidental deletion of resource group directory + * by configfs user. In the future we will employ a + * different approach to handle accidental deletion. + */ + cxi_rx_profile_dec_refcount(rx_prof->cdev, + rx_prof->rx_profile); + + cxi_cfg_drop_item_ref(item); +} + +static void cxi_cfg_rx_prof_dir_free(struct config_item *item) +{ + struct cxi_cfg_rx_prof_dir *rx_prof_dir; + + rx_prof_dir = container_of(item, struct cxi_cfg_rx_prof_dir, + group.cg_item); + kfree(rx_prof_dir); +} + +static struct configfs_group_operations cxi_cfg_rx_prof_dir_ops = { + .make_group = cxi_cfg_rx_prof_dir_make_group, + .drop_item = cxi_cfg_rx_prof_dir_drop_item, +}; + +static struct configfs_item_operations cxi_cfg_rx_prof_dir_item_ops = { + .release = cxi_cfg_rx_prof_dir_free, +}; + +static const struct config_item_type cxi_cfg_rx_prof_dir_type = { + .ct_group_ops = &cxi_cfg_rx_prof_dir_ops, + .ct_item_ops = &cxi_cfg_rx_prof_dir_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct cxi_cfg_rx_prof_dir *alloc_dev_rx_prof_dir(const char *name) +{ + struct cxi_cfg_rx_prof_dir *dev_rx_prof_dir; + + dev_rx_prof_dir = kzalloc(sizeof(*dev_rx_prof_dir), GFP_KERNEL); + if (!dev_rx_prof_dir) + return NULL; + + strscpy(dev_rx_prof_dir->name, name, sizeof(dev_rx_prof_dir->name)); + + config_group_init_type_name(&dev_rx_prof_dir->group, + dev_rx_prof_dir->name, + &cxi_cfg_rx_prof_dir_type); + + return dev_rx_prof_dir; +} + +int cxi_configfs_device_init(struct cass_dev *hw) +{ + struct config_group *parent_group; + struct cxi_cfg_group_item *cxi_cfg_configfs; + struct cxi_cfg_rgroup_dir *rgp_dir; + struct cxi_cfg_rx_prof_dir *rx_prof_dir; + struct cxi_cfg_tx_prof_dir *tx_prof_dir; + int rc; + + parent_group = &cxi_cfg_group_subsys.su_group; + + cxi_cfg_configfs = alloc_dev_subdir(hw->cdev.name); + if (!cxi_cfg_configfs) + return -ENOMEM; + + hw->cfg_dev_dir = cxi_cfg_configfs; + + rc = configfs_register_group(parent_group, + &cxi_cfg_configfs->group); + if (rc) + goto dev_grp_reg_err; + + rgp_dir = alloc_dev_attr1("rgroup"); + if (!rgp_dir) { + rc = -ENOMEM; + goto rgp_dir_alloc_err; + } + + rc = configfs_register_group(&cxi_cfg_configfs->group, + &rgp_dir->group); + if (rc) + goto rgp_dir_reg_err; + + cxi_cfg_configfs->rgp_dir = rgp_dir; + rgp_dir->cdev = &hw->cdev; + + rx_prof_dir = alloc_dev_rx_prof_dir("rx-profile"); + if (!rx_prof_dir) { + rc = -ENOMEM; + goto rx_prof_dir_alloc_err; + } + + rc = configfs_register_group(&cxi_cfg_configfs->group, + &rx_prof_dir->group); + if (rc) + goto rx_prof_dir_reg_err; + + cxi_cfg_configfs->rx_prof_dir = rx_prof_dir; + rx_prof_dir->cdev = &hw->cdev; + + tx_prof_dir = alloc_dev_tx_prof_dir("tx-profile"); + if (!tx_prof_dir) { + rc = -ENOMEM; + goto tx_prof_dir_alloc_err; + } + + rc = configfs_register_group(&cxi_cfg_configfs->group, + &tx_prof_dir->group); + if (rc) + goto tx_prof_dir_reg_err; + + cxi_cfg_configfs->tx_prof_dir = tx_prof_dir; + tx_prof_dir->cdev = &hw->cdev; + + return 0; + +tx_prof_dir_reg_err: + kfree(tx_prof_dir); +tx_prof_dir_alloc_err: + configfs_unregister_group(&rx_prof_dir->group); +rx_prof_dir_reg_err: + kfree(rx_prof_dir); +rx_prof_dir_alloc_err: + configfs_unregister_group(&rgp_dir->group); +rgp_dir_reg_err: + kfree(rgp_dir); +rgp_dir_alloc_err: + configfs_unregister_group(&cxi_cfg_configfs->group); +dev_grp_reg_err: + hw->cfg_dev_dir = NULL; + kfree(cxi_cfg_configfs); + + return rc; +} + +int cxi_configfs_subsys_init(void) +{ + struct configfs_subsystem *subsys; + int rc; + + subsys = &cxi_cfg_group_subsys; + + config_group_init(&subsys->su_group); + mutex_init(&subsys->su_mutex); + + rc = configfs_register_subsystem(subsys); + if (rc) + return rc; + + return 0; +} + +/* + * cg_children is protected by the subsystem's su_mutex: configfs mkdir and + * rmdir add and remove children with it held. configfs_unregister_group() + * takes su_mutex itself, so it can't be called with it held. Look at the + * list under the lock, pin the child so it can't be freed under us, and + * drop the lock before unregistering it. + * + * Returns the first child with a reference held, or NULL if there are none. + */ +static struct config_group * +get_first_child_config_group(struct config_group *parent_group, + struct cxi_cfg_teardown_marker *marker) +{ + struct config_item *child_item; + struct config_group *child_group = NULL; + unsigned long flags; + + mutex_lock(&cxi_cfg_group_subsys.su_mutex); + child_item = list_first_entry_or_null(&parent_group->cg_children, + struct config_item, ci_entry); + if (child_item) { + config_item_get(child_item); + marker->item = child_item; + INIT_LIST_HEAD(&marker->list); + spin_lock_irqsave(&cxi_cfg_teardown_lock, flags); + list_add(&marker->list, &cxi_cfg_teardown_markers); + spin_unlock_irqrestore(&cxi_cfg_teardown_lock, flags); + child_group = container_of(child_item, + struct config_group, cg_item); + } + mutex_unlock(&cxi_cfg_group_subsys.su_mutex); + + return child_group; +} + +static void unregister_all_sub_config_groups_recur(struct config_group *parent_group) +{ + struct config_group *child_group; + struct cxi_cfg_teardown_marker marker; + unsigned long flags; + + /* + * Unregistering a child unlinks it from cg_children, so the list + * is re-read each time round instead of being walked across the + * unlocked section. + */ + while ((child_group = get_first_child_config_group(parent_group, &marker))) { + unregister_all_sub_config_groups_recur(child_group); + configfs_unregister_group(child_group); + + spin_lock_irqsave(&cxi_cfg_teardown_lock, flags); + list_del_init(&marker.list); + spin_unlock_irqrestore(&cxi_cfg_teardown_lock, flags); + + /* The reference taken by get_first_child_config_group() */ + config_item_put(&child_group->cg_item); + /* The creator's reference, as before */ + config_item_put(&child_group->cg_item); + } +} + +void cxi_configfs_cleanup(struct cass_dev *hw) +{ + struct cxi_cfg_group_item *dev_dir = hw->cfg_dev_dir; + + if (!dev_dir) + return; + hw->cfg_dev_dir = NULL; + unregister_all_sub_config_groups_recur(&dev_dir->group); + configfs_unregister_group(&dev_dir->group); + config_item_put(&dev_dir->group.cg_item); +} + +void cxi_configfs_exit(void) +{ + configfs_unregister_subsystem(&cxi_cfg_group_subsys); +}
diff --git a/drivers/net/ethernet/hpe/ss1/cxi_core.c b/drivers/net/ethernet/hpe/ss1/cxi_core.c
new file mode 100644
index 000000000000..d46ab3a1df10
--- /dev/null
+++ b/drivers/net/ethernet/hpe/ss1/cxi_core.c@@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2018,2024 Hewlett Packard Enterprise Development LP */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> + +#include "cxi_core.h" + +#define DEFAULT_PID_BITS MAX_PID_BITS + +/* TODO: Make this a per device configurable. */ +unsigned int pid_bits = DEFAULT_PID_BITS; +module_param(pid_bits, uint, 0444); +MODULE_PARM_DESC(pid_bits, "Width of the PID field within a NIC address"); + +/* System info to determine if a system is mixed (C1 & C2) + * or homogeneous (only C1 or only C2). + */ +unsigned int system_type_identifier; +module_param(system_type_identifier, uint, 0444); +MODULE_PARM_DESC(system_type_identifier, + "Identifies what type of Cassini NICs are present in the system\n" + "0 - Cassini 1 and Cassini 2 (Default)\n" + "1 - Cassini 1 Only\n" + "2 - Cassini 2 Only\n"); + +unsigned int min_free_shift; +module_param(min_free_shift, uint, 0444); +MODULE_PARM_DESC(min_free_shift, "min_free shift (0 to 8)"); + +unsigned int vni_matching; +module_param(vni_matching, uint, 0444); +MODULE_PARM_DESC(vni_matching, "vni matching configuration (0 to 3)"); + +unsigned int tg_threshold[4] = { 0, 64, 128, 256 }; +module_param_array(tg_threshold, uint, NULL, 0644); +MODULE_PARM_DESC(tg_threshold, "Array of 4 LPE append credits. Credits are 0 for immediate append, or 4 to 16383 for delayed append."); + +int untagged_eth_pcp = -1; +module_param(untagged_eth_pcp, int, 0444); +MODULE_PARM_DESC(untagged_eth_pcp, "PCP used by untagged Ethernet frames. Valid PCPs 0-7. Use -1 to use the PCP specified in chosen QOS Profile"); + +bool switch_connected = true; +module_param(switch_connected, bool, 0644); +MODULE_PARM_DESC(switch_connected, "Connected to a switch or another NIC"); + +struct class cxi_class = { + .name = "cxi", +}; + +/* For device naming purposes */ +atomic_t cxi_num = ATOMIC_INIT(-1); + +/* root debugfs directory for all devices */ +struct dentry *cxi_debug_dir; + +/* List of devices and its mutex. */ +static LIST_HEAD(dev_list); +static DEFINE_MUTEX(dev_list_mutex); + +/* List of clients. Protected by dev_list_mutex. */ +static LIST_HEAD(client_list); + +/* Include here as p2p support is optional and we don't build the p2p file + * when it's disabled, but still need the ops struct itself */ +struct cass_p2p_ops p2p_ops = { + .get_pages = NULL, + .put_pages = NULL, + .is_device_mem = NULL, +}; + +int cxi_get_lpe_append_credits(unsigned int lpe_cdt_thresh_id) +{ + if (lpe_cdt_thresh_id >= ARRAY_SIZE(tg_threshold)) + return -EINVAL; + return tg_threshold[lpe_cdt_thresh_id]; +} +EXPORT_SYMBOL(cxi_get_lpe_append_credits); + +/** + * cxi_register_client() - Register a CXI client + * + * @client: client to be registered + */ +int cxi_register_client(struct cxi_client *client) +{ + struct cxi_dev *dev; + + mutex_lock(&dev_list_mutex); + + list_add_tail(&client->list, &client_list); + + list_for_each_entry(dev, &dev_list, dev_list) + if (client->add) + client->add(dev); + + mutex_unlock(&dev_list_mutex); + + return 0; +} +EXPORT_SYMBOL(cxi_register_client); + +/** + * cxi_unregister_client() - Unregister a CXI client + * + * @client: client to be unregistered + */ +void cxi_unregister_client(struct cxi_client *client) +{ + struct cxi_dev *dev; + + mutex_lock(&dev_list_mutex); + + list_for_each_entry_reverse(dev, &dev_list, dev_list) + if (client->remove) + client->remove(dev); + + list_del(&client->list); + + mutex_unlock(&dev_list_mutex); +} +EXPORT_SYMBOL(cxi_unregister_client); + +/* A device driver is declaring a new CXI device */ +void cxi_add_device(struct cxi_dev *cdev) +{ + struct cxi_client *client; + + mutex_lock(&dev_list_mutex); + list_add_tail(&cdev->dev_list, &dev_list); + + list_for_each_entry(client, &client_list, list) + if (client->add) + client->add(cdev); + + mutex_unlock(&dev_list_mutex); +} + +/* A device driver signals it's going away. */ +void cxi_remove_device(struct cxi_dev *cdev) +{ + struct cxi_client *client; + + mutex_lock(&dev_list_mutex); + list_del(&cdev->dev_list); + + list_for_each_entry_reverse(client, &client_list, list) + if (client->remove) + client->remove(cdev); + + mutex_unlock(&dev_list_mutex); +} + +/* Send an asynchronous event to the clients */ +void cxi_send_async_event(struct cxi_dev *cdev, enum cxi_async_event event) +{ + struct cxi_client *client; + + mutex_lock_nested(&dev_list_mutex, SINGLE_DEPTH_NESTING); + + list_for_each_entry(client, &client_list, list) + if (client->async_event) + client->async_event(cdev, event); + + mutex_unlock(&dev_list_mutex); +} + +void cxi_apply_for_all(void (*callback)(struct cxi_dev *dev, void *p), void *p) +{ + struct cxi_dev *dev; + + mutex_lock(&dev_list_mutex); + + list_for_each_entry(dev, &dev_list, dev_list) + (*callback)(dev, p); + + mutex_unlock(&dev_list_mutex); +} + +static int __init cxi_init(void) +{ + int rc; + int i; + + if (pid_bits > MAX_PID_BITS) { + pr_warn("PID count (%u) exceeded max (%u). Using default (%u).\n", + pid_bits, MAX_PID_BITS, DEFAULT_PID_BITS); + pid_bits = DEFAULT_PID_BITS; + } + + if (pid_bits < MIN_PID_BITS) { + pr_warn("PID bits (%u) less than min (%u). Using default (%u).\n", + pid_bits, MIN_PID_BITS, DEFAULT_PID_BITS); + pid_bits = DEFAULT_PID_BITS; + } + + if (untagged_eth_pcp > 7 || untagged_eth_pcp < -1) { + pr_err("Invalid untagged Ethernet PCP value: %u\n", + untagged_eth_pcp); + return -EINVAL; + } + + if (min_free_shift > 8) { + pr_warn("min_free_shift (%d) must be 0 to 8.\n", + min_free_shift); + return -EINVAL; + } + + if (vni_matching > 3) { + pr_warn("vni_matching (%d) must be 0 to 3.\n", vni_matching); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(tg_threshold); i++) { + if ((tg_threshold[i] >= 1 && tg_threshold[i] <= 3) || + tg_threshold[i] > 16383) { + pr_warn("tg_threshold must be 0 or 4 to 16383\n"); + return -EINVAL; + } + } + + cxi_debug_dir = debugfs_create_dir("cxi", NULL); + + rc = class_register(&cxi_class); + if (rc) { + pr_warn("Couldn't create Cray eXascale Interconnect device class\n"); + goto err_class_reg; + } + + rc = cxi_configfs_subsys_init(); + if (rc) { + pr_err("Error while registering configfs subsystem\n"); + goto err_configfs_reg; + } + + rc = hw_register(); + if (rc < 0) { + pr_err("Driver registration failed\n"); + goto err_drv_reg; + } + + if (!(system_type_identifier == CASSINI_1_ONLY || + system_type_identifier == CASSINI_2_ONLY || + system_type_identifier == CASSINI_MIX)) { + pr_warn("Unknown system_type_identifier (%u). Using default (%u).\n", + system_type_identifier, CASSINI_MIX); + system_type_identifier = CASSINI_MIX; + } + + return 0; + +err_drv_reg: + cxi_configfs_exit(); +err_configfs_reg: + class_unregister(&cxi_class); + +err_class_reg: + debugfs_remove(cxi_debug_dir); + + cxi_p2p_fini(); + + return rc; +} + +static void __exit cxi_exit(void) +{ + hw_unregister(); + class_unregister(&cxi_class); + cxi_configfs_exit(); + debugfs_remove(cxi_debug_dir); + cxi_p2p_fini(); +} + +module_init(cxi_init); +module_exit(cxi_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cray Cassini Nic Driver"); +MODULE_AUTHOR("Cray Inc.");
diff --git a/drivers/net/ethernet/hpe/ss1/cxi_link.c b/drivers/net/ethernet/hpe/ss1/cxi_link.c
new file mode 100644
index 000000000000..8d53bbb2f027
--- /dev/null
+++ b/drivers/net/ethernet/hpe/ss1/cxi_link.c@@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cassini LINK + * Copyright 2022, 2024-2026 Hewlett Packard Enterprise Development LP + */ + +#include <linux/kernel.h> +#include <linux/hpe/cxi/cxi.h> + +#include "cass_core.h" +#include "cass_sbl.h" +#include "cass_sl.h" +#include "cxi_ethtool.h" + +static const struct cxi_link_ops cxi_link_ops_sbl = { + .init = cass_sbl_init, + .link_start = cass_sbl_link_start, + .link_fini = cass_sbl_link_fini, + .mode_get = cass_sbl_mode_get, + .mode_set = cass_sbl_mode_set, + .flags_get = cass_sbl_get_debug_flags, + .flags_set = cass_sbl_set_debug_flags, + .flags_supported = LINK_DEBUG_FLAGS, + .link_up = cass_sbl_power_on, + .link_down = cass_sbl_power_off, + .is_pcs_aligned = cass_sbl_pml_pcs_aligned, + .media_config = cass_sbl_media_config, + .media_unconfig = cass_sbl_media_unconfig, + .link_config = cass_sbl_link_config, + .link_reset = cass_sbl_reset, + .link_exit = cass_sbl_exit_legacy, + .pml_recovery_set = cass_sbl_pml_recovery_set, + .is_link_up = cass_sbl_is_link_up, + .eth_name_set = cass_sbl_set_eth_name, +}; + +static void cass_sl_exit_legacy(struct cass_dev *hw) +{ + int rtn = cass_sl_exit(hw); + + if (rtn) + WARN(1, "Cassini SL exit failed: %d\n", rtn); +} + +static const struct cxi_link_ops cxi_link_ops_sl = { + .init = cass_sl_init, + .link_start = cass_sbl_link_start, /* TODO - temporary */ + .link_fini = cass_sl_link_fini, + .mode_get = cass_sl_mode_get, + .mode_set = cass_sl_mode_set, + .flags_get = cass_sl_flags_get, + .flags_set = cass_sl_flags_set, + .flags_supported = 0, + .link_up = cass_sl_link_up, + .link_down = cass_sl_link_down, + .is_pcs_aligned = cass_sl_is_pcs_aligned, + .media_config = cass_sl_media_config, + .media_unconfig = cass_sl_media_unconfig, + .link_config = cass_sl_link_config, + .link_reset = cass_sl_link_down, + .link_exit = cass_sl_exit_legacy, + .pml_recovery_set = cass_sl_pml_recovery_set, + .is_link_up = cass_sl_is_link_up, + .eth_name_set = cass_sl_connect_id_set, +}; + +void cxi_set_link_ops(struct cass_dev *hw) +{ + if (cass_version(hw, CASSINI_1)) + hw->link_ops = &cxi_link_ops_sbl; + else + hw->link_ops = &cxi_link_ops_sl; +} + +void cxi_link_mode_get(struct cxi_dev *cxi_dev, struct cxi_link_info *link_info) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + hw->link_ops->mode_get(hw, link_info); +} +EXPORT_SYMBOL(cxi_link_mode_get); + +void cxi_link_mode_set(struct cxi_dev *cxi_dev, const struct cxi_link_info *link_info) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + hw->link_ops->mode_set(hw, link_info); +} +EXPORT_SYMBOL(cxi_link_mode_set); + +void cxi_link_flags_get(struct cxi_dev *cxi_dev, u32 *flags) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + hw->link_ops->flags_get(hw, flags); +} +EXPORT_SYMBOL(cxi_link_flags_get); + +void cxi_link_flags_set(struct cxi_dev *cxi_dev, u32 clr_flags, u32 set_flags) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + hw->link_ops->flags_set(hw, clr_flags, set_flags); +} +EXPORT_SYMBOL(cxi_link_flags_set); + +u32 cxi_link_flags_supported(struct cxi_dev *cxi_dev) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + return hw->link_ops->flags_supported; +} +EXPORT_SYMBOL(cxi_link_flags_supported); + +void cxi_link_use_unsupported_cable(struct cxi_dev *cxi_dev, bool use) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + cxidev_dbg(&hw->cdev, "use unsupported cable\n"); + + if (use) + hw->sl.link_policy.options |= SL_LINK_POLICY_OPT_USE_UNSUPPORTED_CABLE; + else + hw->sl.link_policy.options &= ~SL_LINK_POLICY_OPT_USE_UNSUPPORTED_CABLE; +} +EXPORT_SYMBOL(cxi_link_use_unsupported_cable); + +void cxi_link_use_supported_ss200_cable(struct cxi_dev *cxi_dev, bool use) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + cxidev_dbg(&hw->cdev, "use supported ss200 cable\n"); + + if (use) + hw->sl.link_policy.options |= SL_LINK_POLICY_OPT_USE_SUPPORTED_SS200_CABLE; + else + hw->sl.link_policy.options &= ~SL_LINK_POLICY_OPT_USE_SUPPORTED_SS200_CABLE; +} +EXPORT_SYMBOL(cxi_link_use_supported_ss200_cable); + +void cxi_link_ignore_media_error(struct cxi_dev *cxi_dev, bool ignore) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + cxidev_dbg(&hw->cdev, "ignore media error\n"); + + if (ignore) + hw->sl.link_policy.options |= SL_LINK_POLICY_OPT_IGNORE_MEDIA_ERROR; + else + hw->sl.link_policy.options &= ~SL_LINK_POLICY_OPT_IGNORE_MEDIA_ERROR; +} +EXPORT_SYMBOL(cxi_link_ignore_media_error); + +void cxi_link_auto_lane_degrade(struct cxi_dev *cxi_dev, bool enable) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + cxidev_dbg(&hw->cdev, "auto lane degrade\n"); + + if (enable) + hw->sl.link_config.options |= SL_LINK_CONFIG_OPT_ALD_ENABLE; + else + hw->sl.link_config.options &= ~SL_LINK_CONFIG_OPT_ALD_ENABLE; +} +EXPORT_SYMBOL(cxi_link_auto_lane_degrade); + +void cxi_pml_recovery_set(struct cxi_dev *cxi_dev, bool set) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + hw->link_ops->pml_recovery_set(hw, set); +} +EXPORT_SYMBOL(cxi_pml_recovery_set); + +void cxi_link_los_lol_hide(struct cxi_dev *cxi_dev, bool enable) +{ + struct cass_dev *hw = container_of(cxi_dev, struct cass_dev, cdev); + + cxidev_dbg(&hw->cdev, "los_lol_hide\n"); + + if (enable) + hw->sl.link_config.options |= SL_LINK_CONFIG_OPT_LOS_LOL_UP_FAIL_HIDE; + else + hw->sl.link_config.options &= ~SL_LINK_CONFIG_OPT_LOS_LOL_UP_FAIL_HIDE; +} +EXPORT_SYMBOL(cxi_link_los_lol_hide); + +/** + * cxi_is_link_up_vf() - Query the PF for the current link state + * + * @cdev: VF CXI device + * + * Return: true if the link is up, false otherwise or on error. + */ +static bool cxi_is_link_up_vf(struct cxi_dev *cdev) +{ + const struct cxi_eth_link_state_get_cmd cmd = { + .op = CXI_OP_ETH_LINK_STATE_GET, + }; + struct cxi_eth_get_link_state_resp resp = {}; + size_t resp_len = sizeof(resp); + int rc; + + rc = cxi_send_msg_to_pf(cdev, &cmd, sizeof(cmd), &resp, &resp_len); + if (rc) + return false; + + return !!resp.link_up; +} + +/** + * cxi_is_link_up() - Returns whether the link is up or down + * + * @cdev: the device + * + * Return: true if the link is up, false otherwise. + */ +bool cxi_is_link_up(struct cxi_dev *cdev) +{ + struct cass_dev *hw = container_of(cdev, struct cass_dev, cdev); + + if (!cdev->is_physfn) + return cxi_is_link_up_vf(cdev); + + return hw->link_ops->is_link_up(hw); +} +EXPORT_SYMBOL(cxi_is_link_up);
--
2.56.0