aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc/marvell/octeontx2-rm/domain_sysfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/marvell/octeontx2-rm/domain_sysfs.c')
-rw-r--r--drivers/soc/marvell/octeontx2-rm/domain_sysfs.c832
1 files changed, 832 insertions, 0 deletions
diff --git a/drivers/soc/marvell/octeontx2-rm/domain_sysfs.c b/drivers/soc/marvell/octeontx2-rm/domain_sysfs.c
new file mode 100644
index 000000000000..9101edea8118
--- /dev/null
+++ b/drivers/soc/marvell/octeontx2-rm/domain_sysfs.c
@@ -0,0 +1,832 @@
+// SPDX-License-Identifier: GPL-2.0
+/* OcteonTX2 RVU Resource Manager driver
+ *
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/sysfs.h>
+#include "domain_sysfs.h"
+#include "otx2_rm.h"
+#include "dpi.h"
+
+#define DOMAIN_NAME_LEN 32
+#define PCI_SCAN_FMT "%04x:%02x:%02x.%02x"
+
+/* The format of DP is: DP(_name, _param_type, _scanf_fmt) */
+#define DOM_PARAM_SPEC \
+DP(ssow, int, "%d") \
+DP(sso, int, "%d") \
+DP(npa, int, "%d") \
+DP(cpt, int, "%d") \
+DP(tim, int, "%d") \
+DP(dpi, int, "%d")
+
+struct domain_params {
+ const char *name;
+#define DP(_name, _type, _1) \
+ _type _name;
+DOM_PARAM_SPEC
+#undef DP
+ const char *ports[RM_MAX_PORTS];
+ u16 port_cnt;
+};
+
+struct domain {
+ char name[DOMAIN_NAME_LEN];
+ struct kobj_attribute domain_id;
+ struct kobj_attribute domain_in_use;
+ /* List of all ports attached to the domain */
+ struct rvu_port *ports;
+ struct kobject *kobj;
+ struct rvu_vf *rvf;
+ int port_count;
+ bool in_use;
+};
+
+struct rvu_port {
+ /* handle in global list of ports associated to all domains */
+ struct list_head list;
+ struct pci_dev *pdev;
+ struct domain *domain;
+};
+
+struct dpi_vf {
+ struct pci_dev *pdev;
+ /* pointer to the kobject which owns this vf */
+ struct kobject *domain_kobj;
+ int vf_id;
+ bool in_use;
+};
+
+struct dpi_info {
+ /* Total number of vfs available */
+ uint8_t num_vfs;
+ /* Free vfs */
+ uint8_t vfs_free;
+ /* Pointer to the vfs available */
+ struct dpi_vf *dpi_vf;
+};
+
+struct domain_sysfs {
+ struct list_head list;
+ struct kobj_attribute create_domain;
+ struct kobj_attribute destroy_domain;
+ struct kobj_attribute pmccntr_el0;
+ /* List of all ports added to all domains. Used for validating if new
+ * domain creation doesn't want to take an already taken port.
+ */
+ struct list_head ports;
+ struct rm_dev *rdev;
+ struct kobject *parent;
+ struct domain *domains;
+ size_t domains_len;
+ struct dpi_info dpi_info;
+};
+
+static DEFINE_MUTEX(domain_sysfs_lock);
+static LIST_HEAD(domain_sysfs_list);
+
+static ssize_t
+domain_id_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ struct domain *dom = container_of(attr, struct domain, domain_id);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", dom->name);
+}
+
+static ssize_t
+domain_in_use_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ struct domain *dom = container_of(attr, struct domain, domain_in_use);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", dom->rvf->in_use);
+}
+
+static int do_destroy_domain(struct domain_sysfs *lsfs, struct domain *domain)
+{
+ struct device *dev = &lsfs->rdev->pdev->dev;
+ int i;
+
+ if (domain->rvf->in_use) {
+ dev_err(dev, "Domain %s is in use.\n", domain->name);
+ return -EBUSY;
+ }
+
+ sysfs_remove_file(domain->kobj, &domain->domain_id.attr);
+ domain->domain_id.attr.mode = 0;
+ sysfs_remove_file(domain->kobj, &domain->domain_in_use.attr);
+ domain->domain_in_use.attr.mode = 0;
+ for (i = 0; i < domain->port_count; i++) {
+ sysfs_remove_link(domain->kobj,
+ pci_name(domain->ports[i].pdev));
+ }
+
+ for (i = 0; i < lsfs->dpi_info.num_vfs; i++) {
+ struct dpi_vf *dpivf_ptr = NULL;
+
+ dpivf_ptr = &lsfs->dpi_info.dpi_vf[i];
+ /* Identify the devices belongs to this domain */
+ if (dpivf_ptr->in_use &&
+ dpivf_ptr->domain_kobj == domain->kobj) {
+ sysfs_remove_link(domain->kobj,
+ pci_name(dpivf_ptr->pdev));
+ dpivf_ptr->in_use = false;
+ dpivf_ptr->domain_kobj = NULL;
+ lsfs->dpi_info.vfs_free++;
+ }
+ }
+
+ sysfs_remove_link(domain->kobj, pci_name(domain->rvf->pdev));
+ kobject_del(domain->kobj);
+ mutex_lock(&lsfs->rdev->lock);
+ // restore limits
+ lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val = 0;
+ lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val = 0;
+ lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val = 0;
+ lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val = 0;
+ lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val = 0;
+ mutex_unlock(&lsfs->rdev->lock);
+
+ mutex_lock(&domain_sysfs_lock);
+ // FREE ALL allocated ports
+ for (i = 0; i < domain->port_count; i++) {
+ list_del(&domain->ports[i].list);
+ pci_dev_put(domain->ports[i].pdev);
+ }
+ kfree(domain->ports);
+ domain->ports = NULL;
+ domain->port_count = 0;
+ domain->in_use = false;
+ domain->name[0] = '\0';
+ mutex_unlock(&domain_sysfs_lock);
+
+ return 0;
+}
+
+static int
+do_create_domain(struct domain_sysfs *lsfs, struct domain_params *dparams)
+{
+ struct device *dev = &lsfs->rdev->pdev->dev;
+ struct domain *domain = NULL;
+ struct rvu_port *ports = NULL, *cur;
+ u32 dom, bus, slot, fn;
+ int old_sso, old_ssow, old_npa, old_cpt, old_tim, device;
+ int res = 0, i;
+
+ /* Validate parameters */
+ if (dparams == NULL)
+ return -EINVAL;
+ if (strnlen(dparams->name, DOMAIN_NAME_LEN) >= DOMAIN_NAME_LEN) {
+ dev_err(dev, "Domain name too long, max %d characters.\n",
+ DOMAIN_NAME_LEN);
+ return -EINVAL;
+ }
+ if (dparams->npa != 1) {
+ dev_err(dev, "Exactly 1 NPA resource required.\n");
+ return -EINVAL;
+ }
+ if (dparams->ssow < 1) {
+ dev_err(dev, "At least 1 SSOW resource required.\n");
+ return -EINVAL;
+ }
+ mutex_lock(&domain_sysfs_lock);
+ /* Find a free domain device */
+ for (i = 0; i < lsfs->domains_len; i++) {
+ if (!strncmp(lsfs->domains[i].name, dparams->name,
+ DOMAIN_NAME_LEN)) {
+ dev_err(dev, "Domain %s exists already.\n",
+ dparams->name);
+ res = -EINVAL;
+ goto err_dom;
+ }
+ if (lsfs->domains[i].in_use == false &&
+ lsfs->domains[i].rvf->in_use == false) {
+ if (domain == NULL)
+ domain = &lsfs->domains[i];
+ }
+ }
+ if (domain == NULL) {
+ dev_err(dev, "No free device to create new domain.\n");
+ res = -ENODEV;
+ goto err_dom;
+ }
+ strncpy(domain->name, dparams->name, DOMAIN_NAME_LEN - 1);
+ domain->in_use = true;
+ /* Verify ports are valid and supported. */
+ if (dparams->port_cnt == 0)
+ goto skip_ports;
+ ports = kcalloc(dparams->port_cnt, sizeof(struct rvu_port), GFP_KERNEL);
+ if (ports == NULL) {
+ dev_err(dev, "Not enough memory.\n");
+ res = -ENOMEM;
+ goto err_ports;
+ }
+ for (i = 0; i < dparams->port_cnt; i++) {
+ if (sscanf(dparams->ports[i], PCI_SCAN_FMT, &dom, &bus, &slot,
+ &fn) != 4) {
+ dev_err(dev, "Invalid port: %s.\n", dparams->ports[i]);
+ res = -EINVAL;
+ goto err_ports;
+ }
+ ports[i].pdev =
+ pci_get_domain_bus_and_slot(dom, bus,
+ PCI_DEVFN(slot, fn));
+ if (ports[i].pdev == NULL) {
+ dev_err(dev, "Unknown port: %s.\n", dparams->ports[i]);
+ res = -ENODEV;
+ goto err_ports;
+ }
+ device = ports[i].pdev->device;
+ if (ports[i].pdev->vendor != PCI_VENDOR_ID_CAVIUM ||
+ (device != PCI_DEVID_OCTEONTX2_RVU_PF &&
+ device != PCI_DEVID_OCTEONTX2_PASS1_RVU_PF &&
+ device != PCI_DEVID_OCTEONTX2_RVU_AFVF &&
+ device != PCI_DEVID_OCTEONTX2_PASS1_RVU_AFVF &&
+ device != PCI_DEVID_OCTEONTX2_RVU_VF &&
+ device != PCI_DEVID_OCTEONTX2_PASS1_RVU_VF)) {
+ dev_err(dev, "Unsupported port: %s.\n",
+ dparams->ports[i]);
+ res = -EINVAL;
+ goto err_ports;
+ }
+ list_for_each_entry(cur, &lsfs->ports, list) {
+ if (cur->pdev != ports[i].pdev)
+ continue;
+ dev_err(dev,
+ "Port %s already assigned to domain %s.\n",
+ dparams->ports[i], cur->domain->name);
+ res = -EBUSY;
+ goto err_ports;
+ }
+ }
+ for (i = 0; i < dparams->port_cnt; i++) {
+ ports[i].domain = domain;
+ list_add(&ports[i].list, &lsfs->ports);
+ }
+ domain->ports = ports;
+ domain->port_count = dparams->port_cnt;
+skip_ports:
+ mutex_unlock(&domain_sysfs_lock);
+ /* Check domain spec against limits for the parent RVU. */
+ mutex_lock(&lsfs->rdev->lock);
+ old_sso = lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val;
+ old_ssow = lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val;
+ old_npa = lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val;
+ old_cpt = lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val;
+ old_tim = lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val;
+#define CHECK_LIMITS(_ls, _val, _n, _idx) do { \
+ if (quotas_get_sum(_ls) + _val - _ls->a[_idx].val > _ls->max_sum) { \
+ dev_err(dev, \
+ "Not enough "_n" LFs, currently used: %lld/%lld\n", \
+ quotas_get_sum(_ls), _ls->max_sum); \
+ res = -ENODEV; \
+ goto err_limits; \
+ } \
+} while (0)
+ CHECK_LIMITS(lsfs->rdev->vf_limits.sso, dparams->sso, "SSO",
+ domain->rvf->vf_id);
+ CHECK_LIMITS(lsfs->rdev->vf_limits.ssow, dparams->ssow, "SSOW",
+ domain->rvf->vf_id);
+ CHECK_LIMITS(lsfs->rdev->vf_limits.npa, dparams->npa, "NPA",
+ domain->rvf->vf_id);
+ CHECK_LIMITS(lsfs->rdev->vf_limits.cpt, dparams->cpt, "CPT",
+ domain->rvf->vf_id);
+ CHECK_LIMITS(lsfs->rdev->vf_limits.tim, dparams->tim, "TIM",
+ domain->rvf->vf_id);
+ if (dparams->dpi > lsfs->dpi_info.vfs_free) {
+ dev_err(dev,
+ "Not enough DPI VFS, currently used:%d/%d\n",
+ lsfs->dpi_info.num_vfs -
+ lsfs->dpi_info.vfs_free,
+ lsfs->dpi_info.num_vfs);
+ res = -ENODEV;
+ goto err_limits;
+ }
+
+ /* Now that checks are done, update the limits */
+ lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val = dparams->sso;
+ lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val = dparams->ssow;
+ lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val = dparams->npa;
+ lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val = dparams->cpt;
+ lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val = dparams->tim;
+ lsfs->dpi_info.vfs_free -= dparams->dpi;
+ mutex_unlock(&lsfs->rdev->lock);
+
+ /* Set it up according to user spec */
+ domain->kobj = kobject_create_and_add(dparams->name, lsfs->parent);
+ if (domain->kobj == NULL) {
+ dev_err(dev, "Failed to create domain directory.\n");
+ res = -ENOMEM;
+ goto err_kobject_create;
+ }
+ res = sysfs_create_link(domain->kobj, &domain->rvf->pdev->dev.kobj,
+ pci_name(domain->rvf->pdev));
+ if (res < 0) {
+ dev_err(dev, "Failed to create dev links for domain %s.\n",
+ domain->name);
+ res = -ENOMEM;
+ goto err_dom_dev_symlink;
+ }
+ for (i = 0; i < dparams->port_cnt; i++) {
+ res = sysfs_create_link(domain->kobj, &ports[i].pdev->dev.kobj,
+ pci_name(ports[i].pdev));
+ if (res < 0) {
+ dev_err(dev,
+ "Failed to create dev links for domain %s.\n",
+ domain->name);
+ res = -ENOMEM;
+ goto err_dom_port_symlink;
+ }
+ }
+ /* Create symlinks for dpi vfs in domain */
+ for (i = 0; i < dparams->dpi; i++) {
+ struct dpi_vf *dpivf_ptr = NULL;
+ int vf_idx;
+
+ for (vf_idx = 0; vf_idx < lsfs->dpi_info.num_vfs;
+ vf_idx++) {
+ /* Find available dpi vfs and create symlinks */
+ dpivf_ptr = &lsfs->dpi_info.dpi_vf[vf_idx];
+ if (dpivf_ptr->in_use)
+ continue;
+ else
+ break;
+ }
+ res = sysfs_create_link(domain->kobj,
+ &dpivf_ptr->pdev->dev.kobj,
+ pci_name(dpivf_ptr->pdev));
+ if (res < 0) {
+ dev_err(dev,
+ "Failed to create DPI dev links for domain %s\n",
+ domain->name);
+ res = -ENOMEM;
+ goto err_dpi_symlink;
+ }
+ dpivf_ptr->domain_kobj = domain->kobj;
+ dpivf_ptr->in_use = true;
+ }
+
+ domain->domain_in_use.attr.mode = 0444;
+ domain->domain_in_use.attr.name = "domain_in_use";
+ domain->domain_in_use.show = domain_in_use_show;
+ res = sysfs_create_file(domain->kobj, &domain->domain_in_use.attr);
+ if (res < 0) {
+ dev_err(dev,
+ "Failed to create domain_in_use file for domain %s.\n",
+ domain->name);
+ res = -ENOMEM;
+ goto err_dom_in_use;
+ }
+
+ domain->domain_id.attr.mode = 0444;
+ domain->domain_id.attr.name = "domain_id";
+ domain->domain_id.show = domain_id_show;
+ res = sysfs_create_file(domain->kobj, &domain->domain_id.attr);
+ if (res < 0) {
+ dev_err(dev, "Failed to create domain_id file for domain %s.\n",
+ domain->name);
+ res = -ENOMEM;
+ goto err_dom_id;
+ }
+
+ return res;
+
+err_dom_id:
+ domain->domain_id.attr.mode = 0;
+ sysfs_remove_file(domain->kobj, &domain->domain_in_use.attr);
+err_dom_in_use:
+ domain->domain_in_use.attr.mode = 0;
+err_dpi_symlink:
+ for (i = 0; i < lsfs->dpi_info.num_vfs; i++) {
+ struct dpi_vf *dpivf_ptr = NULL;
+
+ dpivf_ptr = &lsfs->dpi_info.dpi_vf[i];
+ /* Identify the devices belongs to this domain */
+ if (dpivf_ptr->in_use &&
+ dpivf_ptr->domain_kobj == domain->kobj) {
+ sysfs_remove_link(domain->kobj,
+ pci_name(dpivf_ptr->pdev));
+ dpivf_ptr->in_use = false;
+ dpivf_ptr->domain_kobj = NULL;
+ }
+ }
+err_dom_port_symlink:
+ for (i = 0; i < dparams->port_cnt; i++)
+ sysfs_remove_link(domain->kobj, pci_name(ports[i].pdev));
+ sysfs_remove_link(domain->kobj, pci_name(domain->rvf->pdev));
+err_dom_dev_symlink:
+ kobject_del(domain->kobj);
+err_kobject_create:
+ mutex_lock(&lsfs->rdev->lock);
+err_limits:
+ // restore limits
+ lsfs->rdev->vf_limits.sso->a[domain->rvf->vf_id].val = old_sso;
+ lsfs->rdev->vf_limits.ssow->a[domain->rvf->vf_id].val = old_ssow;
+ lsfs->rdev->vf_limits.npa->a[domain->rvf->vf_id].val = old_npa;
+ lsfs->rdev->vf_limits.cpt->a[domain->rvf->vf_id].val = old_cpt;
+ lsfs->rdev->vf_limits.tim->a[domain->rvf->vf_id].val = old_tim;
+ lsfs->dpi_info.vfs_free += dparams->dpi;
+ mutex_unlock(&lsfs->rdev->lock);
+ mutex_lock(&domain_sysfs_lock);
+err_ports:
+ // FREE ALL allocated ports
+ for (i = 0; i < dparams->port_cnt; i++) {
+ if (ports[i].pdev == NULL)
+ break;
+ if (ports[i].domain != NULL)
+ list_del(&ports[i].list);
+ pci_dev_put(ports[i].pdev);
+ }
+ kfree(ports);
+ domain->ports = NULL;
+ domain->port_count = 0;
+ domain->in_use = false;
+ domain->name[0] = '\0';
+err_dom:
+ mutex_unlock(&domain_sysfs_lock);
+ return res;
+}
+
+static ssize_t
+destroy_domain_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct domain_sysfs *lsfs =
+ container_of(attr, struct domain_sysfs, destroy_domain);
+ struct device *dev = &lsfs->rdev->pdev->dev;
+ struct domain *domain = NULL;
+ char name[DOMAIN_NAME_LEN], *name_ptr;
+ int i, res;
+
+ strncpy(name, buf, DOMAIN_NAME_LEN - 1);
+ name_ptr = strim(name);
+ if (strlen(name_ptr) == 0) {
+ dev_err(dev, "Empty domain name.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&domain_sysfs_lock);
+ /* Find a free domain device */
+ for (i = 0; i < lsfs->domains_len; i++) {
+ if (!strncmp(lsfs->domains[i].name, name_ptr,
+ DOMAIN_NAME_LEN)) {
+ domain = &lsfs->domains[i];
+ break;
+ }
+ }
+ if (domain == NULL) {
+ dev_err(dev, "Domain '%s' doesn't exist.\n", name);
+ res = -EINVAL;
+ goto err_dom;
+ }
+ mutex_unlock(&domain_sysfs_lock);
+
+ res = do_destroy_domain(lsfs, domain);
+ if (res == 0)
+ res = count;
+err_dom:
+ mutex_unlock(&domain_sysfs_lock);
+ return res;
+}
+
+static ssize_t
+create_domain_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct domain_params *dparams = NULL;
+ struct domain_sysfs *lsfs =
+ container_of(attr, struct domain_sysfs, create_domain);
+ struct device *dev = &lsfs->rdev->pdev->dev;
+ int res = 0;
+ char *start;
+ char *end;
+ char *ptr = NULL;
+ const char *name;
+ char *errmsg = "Invalid domain specification format.";
+
+ if (strlen(buf) == 0) {
+ dev_err(dev, "Empty domain spec.\n");
+ return -EINVAL;
+ }
+
+ dparams = kzalloc(sizeof(*dparams), GFP_KERNEL);
+ if (dparams == NULL) {
+ errmsg = "Not enough memory";
+ res = -ENOMEM;
+ goto error;
+ }
+
+ end = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (end == NULL) {
+ errmsg = "Not enough memory";
+ res = -ENOMEM;
+ goto error;
+ }
+
+ ptr = end;
+ memcpy(end, buf, count);
+
+ name = strsep(&end, ";");
+ if (end == NULL) {
+ res = -EINVAL;
+ goto error;
+ }
+
+ dparams->name = name;
+
+ for (;;) {
+ start = strsep(&end, ";");
+ if (start == NULL)
+ break;
+ start = strim(start);
+ if (!*start)
+ continue;
+
+ if (!strncmp(strim(start), "port", sizeof("port") - 1)) {
+ strsep(&start, ":");
+ if (dparams->port_cnt >= RM_MAX_PORTS)
+ goto error;
+ dparams->ports[dparams->port_cnt++] = strim(start);
+ }
+ #define DP(_name, _1, _fmt) \
+ else if (!strncmp(strim(start), #_name, \
+ sizeof(#_name) - 1)) { \
+ strsep(&start, ":"); \
+ start = strim(start); \
+ res = sscanf(start, _fmt, &dparams->_name); \
+ if (res != 1) \
+ goto error; \
+ continue; \
+ }
+ DOM_PARAM_SPEC
+ #undef DP
+ else {
+ res = -EINVAL;
+ goto error;
+ }
+ }
+ res = do_create_domain(lsfs, dparams);
+ if (res < 0) {
+ errmsg = "Failed to create application domain.";
+ goto error;
+ } else
+ res = count;
+error:
+ if (res < 0)
+ dev_err(dev, "%s\n", errmsg);
+ kfree(ptr);
+ kfree(dparams);
+ return res;
+}
+
+static int dpivf_sysfs_create(struct domain_sysfs *lsfs)
+{
+ struct dpi_info *dpi_info = &lsfs->dpi_info;
+ struct dpi_vf *dpivf_ptr = NULL;
+ struct pci_dev *pdev = lsfs->rdev->pdev;
+ struct pci_dev *vdev = NULL;
+ uint8_t vf_idx = 0;
+
+ dpi_info->dpi_vf = kcalloc(DPI_MAX_VFS,
+ sizeof(struct dpi_vf), GFP_KERNEL);
+ if (dpi_info->dpi_vf == NULL)
+ return -ENOMEM;
+
+ /* Get available DPI vfs */
+ while ((vdev = pci_get_device(pdev->vendor,
+ PCI_DEVID_OCTEONTX2_DPI_VF, vdev))) {
+ if (!vdev->is_virtfn)
+ continue;
+ else {
+ dpivf_ptr = &dpi_info->dpi_vf[vf_idx];
+ dpivf_ptr->pdev = vdev;
+ dpivf_ptr->vf_id = vf_idx;
+ dpivf_ptr->in_use = false;
+ vf_idx++;
+ }
+ }
+ dpi_info->num_vfs = vf_idx;
+ dpi_info->vfs_free = vf_idx;
+ return 0;
+}
+
+static void dpivf_sysfs_destroy(struct domain_sysfs *lsfs)
+{
+ struct dpi_info *dpi_info = &lsfs->dpi_info;
+ struct dpi_vf *dpivf_ptr = NULL;
+ uint8_t vf_idx = 0;
+
+ if (dpi_info->num_vfs == 0)
+ goto free_mem;
+ else {
+ for (vf_idx = 0; vf_idx < dpi_info->num_vfs; vf_idx++) {
+ dpivf_ptr = &dpi_info->dpi_vf[vf_idx];
+ pci_dev_put(dpivf_ptr->pdev);
+ dpivf_ptr->pdev = NULL;
+ vf_idx++;
+ }
+ }
+ dpi_info->num_vfs = 0;
+
+free_mem:
+ kfree(dpi_info->dpi_vf);
+ dpi_info->dpi_vf = NULL;
+}
+
+
+static void enable_pmccntr_el0(void *data)
+{
+ u64 val;
+ /* Disable cycle counter overflow interrupt */
+ asm volatile("mrs %0, pmintenset_el1" : "=r" (val));
+ val &= ~BIT_ULL(31);
+ asm volatile("msr pmintenset_el1, %0" : : "r" (val));
+ /* Enable cycle counter */
+ asm volatile("mrs %0, pmcntenset_el0" : "=r" (val));
+ val |= BIT_ULL(31);
+ asm volatile("msr pmcntenset_el0, %0" :: "r" (val));
+ /* Enable user-mode access to cycle counters. */
+ asm volatile("mrs %0, pmuserenr_el0" : "=r" (val));
+ val |= BIT(2) | BIT(0);
+ asm volatile("msr pmuserenr_el0, %0" : : "r"(val));
+ /* Start cycle counter */
+ asm volatile("mrs %0, pmcr_el0" : "=r" (val));
+ val |= BIT(0);
+ isb();
+ asm volatile("msr pmcr_el0, %0" : : "r" (val));
+ asm volatile("mrs %0, pmccfiltr_el0" : "=r" (val));
+ val |= BIT(27);
+ asm volatile("msr pmccfiltr_el0, %0" : : "r" (val));
+}
+
+static void disable_pmccntr_el0(void *data)
+{
+ u64 val;
+ /* Disable cycle counter */
+ asm volatile("mrs %0, pmcntenset_el0" : "=r" (val));
+ val &= ~BIT_ULL(31);
+ asm volatile("msr pmcntenset_el0, %0" :: "r" (val));
+ /* Disable user-mode access to counters. */
+ asm volatile("mrs %0, pmuserenr_el0" : "=r" (val));
+ val &= ~(BIT(2) | BIT(0));
+ asm volatile("msr pmuserenr_el0, %0" : : "r"(val));
+}
+
+static ssize_t
+enadis_pmccntr_el0_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct domain_sysfs *lsfs = container_of(attr, struct domain_sysfs,
+ pmccntr_el0);
+ struct device *dev = &lsfs->rdev->pdev->dev;
+ char tmp_buf[64];
+ long enable = 0;
+ char *tmp_ptr;
+ ssize_t used;
+
+ strlcpy(tmp_buf, buf, 64);
+ used = strlen(tmp_buf);
+ tmp_ptr = strim(tmp_buf);
+ if (kstrtol(tmp_ptr, 0, &enable)) {
+ dev_err(dev, "Invalid value, expected 1/0\n");
+ return -EIO;
+ }
+
+ if (enable)
+ on_each_cpu(enable_pmccntr_el0, NULL, 1);
+ else
+ on_each_cpu(disable_pmccntr_el0, NULL, 1);
+
+ return count;
+}
+
+static void check_pmccntr_el0(void *data)
+{
+ int *out = data;
+ u64 val;
+
+ asm volatile("mrs %0, pmuserenr_el0" : "=r" (val));
+ *out = *out & !!(val & (BIT(2) | BIT(0)));
+}
+
+static ssize_t
+enadis_pmccntr_el0_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int out = 1;
+
+ on_each_cpu(check_pmccntr_el0, &out, 1);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", out);
+}
+
+int domain_sysfs_create(struct rm_dev *rm)
+{
+ struct domain_sysfs *lsfs;
+ int res = 0, i;
+
+ if (rm == NULL || rm->num_vfs == 0)
+ return -EINVAL;
+
+ lsfs = kzalloc(sizeof(*lsfs), GFP_KERNEL);
+ if (lsfs == NULL) {
+ res = -ENOMEM;
+ goto err_lsfs_alloc;
+ }
+
+ INIT_LIST_HEAD(&lsfs->ports);
+ lsfs->rdev = rm;
+ lsfs->domains_len = rm->num_vfs;
+ lsfs->domains =
+ kcalloc(lsfs->domains_len, sizeof(struct domain), GFP_KERNEL);
+ if (lsfs->domains == NULL)
+ goto err_domains_alloc;
+ for (i = 0; i < lsfs->domains_len; i++)
+ lsfs->domains[i].rvf = &rm->vf_info[i];
+
+ lsfs->create_domain.attr.name = "create_domain";
+ lsfs->create_domain.attr.mode = 0200;
+ lsfs->create_domain.store = create_domain_store;
+ res = sysfs_create_file(&rm->pdev->dev.kobj, &lsfs->create_domain.attr);
+ if (res)
+ goto err_create_domain;
+
+ lsfs->destroy_domain.attr.name = "destroy_domain";
+ lsfs->destroy_domain.attr.mode = 0200;
+ lsfs->destroy_domain.store = destroy_domain_store;
+ res = sysfs_create_file(&rm->pdev->dev.kobj,
+ &lsfs->destroy_domain.attr);
+ if (res)
+ goto err_destroy_domain;
+
+ lsfs->pmccntr_el0.attr.name = "pmccntr_el0";
+ lsfs->pmccntr_el0.attr.mode = 0644;
+ lsfs->pmccntr_el0.show = enadis_pmccntr_el0_show;
+ lsfs->pmccntr_el0.store = enadis_pmccntr_el0_store;
+ res = sysfs_create_file(&rm->pdev->dev.kobj, &lsfs->pmccntr_el0.attr);
+ if (res)
+ goto err_pmccntr_el0;
+
+ lsfs->parent = &rm->pdev->dev.kobj;
+
+ res = dpivf_sysfs_create(lsfs);
+ if (res)
+ goto err_dpivf_sysfs_create;
+
+ mutex_lock(&domain_sysfs_lock);
+ list_add_tail(&lsfs->list, &domain_sysfs_list);
+ mutex_unlock(&domain_sysfs_lock);
+
+ return 0;
+
+err_dpivf_sysfs_create:
+ sysfs_remove_file(&rm->pdev->dev.kobj, &lsfs->pmccntr_el0.attr);
+err_pmccntr_el0:
+ sysfs_remove_file(&rm->pdev->dev.kobj, &lsfs->destroy_domain.attr);
+err_destroy_domain:
+ sysfs_remove_file(&rm->pdev->dev.kobj, &lsfs->create_domain.attr);
+err_create_domain:
+ kfree(lsfs->domains);
+err_domains_alloc:
+ kfree(lsfs);
+err_lsfs_alloc:
+ return res;
+}
+
+void domain_sysfs_destroy(struct rm_dev *rm)
+{
+ struct list_head *pos, *n;
+ struct domain_sysfs *lsfs;
+
+ if (rm == NULL)
+ return;
+
+ mutex_lock(&domain_sysfs_lock);
+ list_for_each_safe(pos, n, &domain_sysfs_list) {
+ lsfs = container_of(pos, struct domain_sysfs, list);
+ if (lsfs->rdev == rm) {
+ list_del(pos);
+ break;
+ }
+ lsfs = NULL;
+ }
+ mutex_unlock(&domain_sysfs_lock);
+
+ if (lsfs == NULL)
+ return;
+
+ dpivf_sysfs_destroy(lsfs);
+
+ if (lsfs->pmccntr_el0.attr.mode != 0)
+ sysfs_remove_file(lsfs->parent, &lsfs->pmccntr_el0.attr);
+ if (lsfs->destroy_domain.attr.mode != 0)
+ sysfs_remove_file(lsfs->parent, &lsfs->destroy_domain.attr);
+ if (lsfs->create_domain.attr.mode != 0)
+ sysfs_remove_file(lsfs->parent, &lsfs->create_domain.attr);
+
+ kfree(lsfs->domains);
+ kfree(lsfs);
+}