[PATCH 20/23] net: hpe: ss1: add the CXI core interfaces

From: Doug Ledford

Date: Fri Oct 30 2026 - 17:09:29 EST


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 <sandeep.bhoot@xxxxxxx>
Signed-off-by: Sandeep Bhoot <sandeep.bhoot@xxxxxxx>
Co-developed-by: Mamila Santosh <santosh-kumar.mamila@xxxxxxx>
Signed-off-by: Mamila Santosh <santosh-kumar.mamila@xxxxxxx>
Signed-off-by: Doug Ledford <doug.ledford@xxxxxxx>
---
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