diff options
Diffstat (limited to 'drivers/soc/marvell/octeontx2-rm/domain_sysfs.c')
-rw-r--r-- | drivers/soc/marvell/octeontx2-rm/domain_sysfs.c | 832 |
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); +} |