Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * nd_perf.c: NVDIMM Device Performance Monitoring Unit support
0004  *
0005  * Perf interface to expose nvdimm performance stats.
0006  *
0007  * Copyright (C) 2021 IBM Corporation
0008  */
0009 
0010 #define pr_fmt(fmt) "nvdimm_pmu: " fmt
0011 
0012 #include <linux/nd.h>
0013 #include <linux/platform_device.h>
0014 
0015 #define EVENT(_name, _code)     enum{_name = _code}
0016 
0017 /*
0018  * NVDIMM Events codes.
0019  */
0020 
0021 /* Controller Reset Count */
0022 EVENT(CTL_RES_CNT,      0x1);
0023 /* Controller Reset Elapsed Time */
0024 EVENT(CTL_RES_TM,       0x2);
0025 /* Power-on Seconds */
0026 EVENT(POWERON_SECS,     0x3);
0027 /* Life Remaining */
0028 EVENT(MEM_LIFE,     0x4);
0029 /* Critical Resource Utilization */
0030 EVENT(CRI_RES_UTIL,     0x5);
0031 /* Host Load Count */
0032 EVENT(HOST_L_CNT,       0x6);
0033 /* Host Store Count */
0034 EVENT(HOST_S_CNT,       0x7);
0035 /* Host Store Duration */
0036 EVENT(HOST_S_DUR,       0x8);
0037 /* Host Load Duration */
0038 EVENT(HOST_L_DUR,       0x9);
0039 /* Media Read Count */
0040 EVENT(MED_R_CNT,        0xa);
0041 /* Media Write Count */
0042 EVENT(MED_W_CNT,        0xb);
0043 /* Media Read Duration */
0044 EVENT(MED_R_DUR,        0xc);
0045 /* Media Write Duration */
0046 EVENT(MED_W_DUR,        0xd);
0047 /* Cache Read Hit Count */
0048 EVENT(CACHE_RH_CNT,     0xe);
0049 /* Cache Write Hit Count */
0050 EVENT(CACHE_WH_CNT,     0xf);
0051 /* Fast Write Count */
0052 EVENT(FAST_W_CNT,       0x10);
0053 
0054 NVDIMM_EVENT_ATTR(ctl_res_cnt,      CTL_RES_CNT);
0055 NVDIMM_EVENT_ATTR(ctl_res_tm,       CTL_RES_TM);
0056 NVDIMM_EVENT_ATTR(poweron_secs,     POWERON_SECS);
0057 NVDIMM_EVENT_ATTR(mem_life,     MEM_LIFE);
0058 NVDIMM_EVENT_ATTR(cri_res_util,     CRI_RES_UTIL);
0059 NVDIMM_EVENT_ATTR(host_l_cnt,       HOST_L_CNT);
0060 NVDIMM_EVENT_ATTR(host_s_cnt,       HOST_S_CNT);
0061 NVDIMM_EVENT_ATTR(host_s_dur,       HOST_S_DUR);
0062 NVDIMM_EVENT_ATTR(host_l_dur,       HOST_L_DUR);
0063 NVDIMM_EVENT_ATTR(med_r_cnt,        MED_R_CNT);
0064 NVDIMM_EVENT_ATTR(med_w_cnt,        MED_W_CNT);
0065 NVDIMM_EVENT_ATTR(med_r_dur,        MED_R_DUR);
0066 NVDIMM_EVENT_ATTR(med_w_dur,        MED_W_DUR);
0067 NVDIMM_EVENT_ATTR(cache_rh_cnt,     CACHE_RH_CNT);
0068 NVDIMM_EVENT_ATTR(cache_wh_cnt,     CACHE_WH_CNT);
0069 NVDIMM_EVENT_ATTR(fast_w_cnt,       FAST_W_CNT);
0070 
0071 static struct attribute *nvdimm_events_attr[] = {
0072     NVDIMM_EVENT_PTR(CTL_RES_CNT),
0073     NVDIMM_EVENT_PTR(CTL_RES_TM),
0074     NVDIMM_EVENT_PTR(POWERON_SECS),
0075     NVDIMM_EVENT_PTR(MEM_LIFE),
0076     NVDIMM_EVENT_PTR(CRI_RES_UTIL),
0077     NVDIMM_EVENT_PTR(HOST_L_CNT),
0078     NVDIMM_EVENT_PTR(HOST_S_CNT),
0079     NVDIMM_EVENT_PTR(HOST_S_DUR),
0080     NVDIMM_EVENT_PTR(HOST_L_DUR),
0081     NVDIMM_EVENT_PTR(MED_R_CNT),
0082     NVDIMM_EVENT_PTR(MED_W_CNT),
0083     NVDIMM_EVENT_PTR(MED_R_DUR),
0084     NVDIMM_EVENT_PTR(MED_W_DUR),
0085     NVDIMM_EVENT_PTR(CACHE_RH_CNT),
0086     NVDIMM_EVENT_PTR(CACHE_WH_CNT),
0087     NVDIMM_EVENT_PTR(FAST_W_CNT),
0088     NULL
0089 };
0090 
0091 static struct attribute_group nvdimm_pmu_events_group = {
0092     .name = "events",
0093     .attrs = nvdimm_events_attr,
0094 };
0095 
0096 PMU_FORMAT_ATTR(event, "config:0-4");
0097 
0098 static struct attribute *nvdimm_pmu_format_attr[] = {
0099     &format_attr_event.attr,
0100     NULL,
0101 };
0102 
0103 static struct attribute_group nvdimm_pmu_format_group = {
0104     .name = "format",
0105     .attrs = nvdimm_pmu_format_attr,
0106 };
0107 
0108 ssize_t nvdimm_events_sysfs_show(struct device *dev,
0109                  struct device_attribute *attr, char *page)
0110 {
0111     struct perf_pmu_events_attr *pmu_attr;
0112 
0113     pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
0114 
0115     return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
0116 }
0117 
0118 static ssize_t nvdimm_pmu_cpumask_show(struct device *dev,
0119                        struct device_attribute *attr, char *buf)
0120 {
0121     struct pmu *pmu = dev_get_drvdata(dev);
0122     struct nvdimm_pmu *nd_pmu;
0123 
0124     nd_pmu = container_of(pmu, struct nvdimm_pmu, pmu);
0125 
0126     return cpumap_print_to_pagebuf(true, buf, cpumask_of(nd_pmu->cpu));
0127 }
0128 
0129 static int nvdimm_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node)
0130 {
0131     struct nvdimm_pmu *nd_pmu;
0132     u32 target;
0133     int nodeid;
0134     const struct cpumask *cpumask;
0135 
0136     nd_pmu = hlist_entry_safe(node, struct nvdimm_pmu, node);
0137 
0138     /* Clear it, incase given cpu is set in nd_pmu->arch_cpumask */
0139     cpumask_test_and_clear_cpu(cpu, &nd_pmu->arch_cpumask);
0140 
0141     /*
0142      * If given cpu is not same as current designated cpu for
0143      * counter access, just return.
0144      */
0145     if (cpu != nd_pmu->cpu)
0146         return 0;
0147 
0148     /* Check for any active cpu in nd_pmu->arch_cpumask */
0149     target = cpumask_any(&nd_pmu->arch_cpumask);
0150 
0151     /*
0152      * Incase we don't have any active cpu in nd_pmu->arch_cpumask,
0153      * check in given cpu's numa node list.
0154      */
0155     if (target >= nr_cpu_ids) {
0156         nodeid = cpu_to_node(cpu);
0157         cpumask = cpumask_of_node(nodeid);
0158         target = cpumask_any_but(cpumask, cpu);
0159     }
0160     nd_pmu->cpu = target;
0161 
0162     /* Migrate nvdimm pmu events to the new target cpu if valid */
0163     if (target >= 0 && target < nr_cpu_ids)
0164         perf_pmu_migrate_context(&nd_pmu->pmu, cpu, target);
0165 
0166     return 0;
0167 }
0168 
0169 static int nvdimm_pmu_cpu_online(unsigned int cpu, struct hlist_node *node)
0170 {
0171     struct nvdimm_pmu *nd_pmu;
0172 
0173     nd_pmu = hlist_entry_safe(node, struct nvdimm_pmu, node);
0174 
0175     if (nd_pmu->cpu >= nr_cpu_ids)
0176         nd_pmu->cpu = cpu;
0177 
0178     return 0;
0179 }
0180 
0181 static int create_cpumask_attr_group(struct nvdimm_pmu *nd_pmu)
0182 {
0183     struct perf_pmu_events_attr *pmu_events_attr;
0184     struct attribute **attrs_group;
0185     struct attribute_group *nvdimm_pmu_cpumask_group;
0186 
0187     pmu_events_attr = kzalloc(sizeof(*pmu_events_attr), GFP_KERNEL);
0188     if (!pmu_events_attr)
0189         return -ENOMEM;
0190 
0191     attrs_group = kzalloc(2 * sizeof(struct attribute *), GFP_KERNEL);
0192     if (!attrs_group) {
0193         kfree(pmu_events_attr);
0194         return -ENOMEM;
0195     }
0196 
0197     /* Allocate memory for cpumask attribute group */
0198     nvdimm_pmu_cpumask_group = kzalloc(sizeof(*nvdimm_pmu_cpumask_group), GFP_KERNEL);
0199     if (!nvdimm_pmu_cpumask_group) {
0200         kfree(pmu_events_attr);
0201         kfree(attrs_group);
0202         return -ENOMEM;
0203     }
0204 
0205     sysfs_attr_init(&pmu_events_attr->attr.attr);
0206     pmu_events_attr->attr.attr.name = "cpumask";
0207     pmu_events_attr->attr.attr.mode = 0444;
0208     pmu_events_attr->attr.show = nvdimm_pmu_cpumask_show;
0209     attrs_group[0] = &pmu_events_attr->attr.attr;
0210     attrs_group[1] = NULL;
0211 
0212     nvdimm_pmu_cpumask_group->attrs = attrs_group;
0213     nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR] = nvdimm_pmu_cpumask_group;
0214     return 0;
0215 }
0216 
0217 static int nvdimm_pmu_cpu_hotplug_init(struct nvdimm_pmu *nd_pmu)
0218 {
0219     int nodeid, rc;
0220     const struct cpumask *cpumask;
0221 
0222     /*
0223      * Incase of cpu hotplug feature, arch specific code
0224      * can provide required cpumask which can be used
0225      * to get designatd cpu for counter access.
0226      * Check for any active cpu in nd_pmu->arch_cpumask.
0227      */
0228     if (!cpumask_empty(&nd_pmu->arch_cpumask)) {
0229         nd_pmu->cpu = cpumask_any(&nd_pmu->arch_cpumask);
0230     } else {
0231         /* pick active cpu from the cpumask of device numa node. */
0232         nodeid = dev_to_node(nd_pmu->dev);
0233         cpumask = cpumask_of_node(nodeid);
0234         nd_pmu->cpu = cpumask_any(cpumask);
0235     }
0236 
0237     rc = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "perf/nvdimm:online",
0238                      nvdimm_pmu_cpu_online, nvdimm_pmu_cpu_offline);
0239 
0240     if (rc < 0)
0241         return rc;
0242 
0243     nd_pmu->cpuhp_state = rc;
0244 
0245     /* Register the pmu instance for cpu hotplug */
0246     rc = cpuhp_state_add_instance_nocalls(nd_pmu->cpuhp_state, &nd_pmu->node);
0247     if (rc) {
0248         cpuhp_remove_multi_state(nd_pmu->cpuhp_state);
0249         return rc;
0250     }
0251 
0252     /* Create cpumask attribute group */
0253     rc = create_cpumask_attr_group(nd_pmu);
0254     if (rc) {
0255         cpuhp_state_remove_instance_nocalls(nd_pmu->cpuhp_state, &nd_pmu->node);
0256         cpuhp_remove_multi_state(nd_pmu->cpuhp_state);
0257         return rc;
0258     }
0259 
0260     return 0;
0261 }
0262 
0263 static void nvdimm_pmu_free_hotplug_memory(struct nvdimm_pmu *nd_pmu)
0264 {
0265     cpuhp_state_remove_instance_nocalls(nd_pmu->cpuhp_state, &nd_pmu->node);
0266     cpuhp_remove_multi_state(nd_pmu->cpuhp_state);
0267 
0268     if (nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR])
0269         kfree(nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR]->attrs);
0270     kfree(nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR]);
0271 }
0272 
0273 int register_nvdimm_pmu(struct nvdimm_pmu *nd_pmu, struct platform_device *pdev)
0274 {
0275     int rc;
0276 
0277     if (!nd_pmu || !pdev)
0278         return -EINVAL;
0279 
0280     /* event functions like add/del/read/event_init and pmu name should not be NULL */
0281     if (WARN_ON_ONCE(!(nd_pmu->pmu.event_init && nd_pmu->pmu.add &&
0282                nd_pmu->pmu.del && nd_pmu->pmu.read && nd_pmu->pmu.name)))
0283         return -EINVAL;
0284 
0285     nd_pmu->pmu.attr_groups = kzalloc((NVDIMM_PMU_NULL_ATTR + 1) *
0286                       sizeof(struct attribute_group *), GFP_KERNEL);
0287     if (!nd_pmu->pmu.attr_groups)
0288         return -ENOMEM;
0289 
0290     /*
0291      * Add platform_device->dev pointer to nvdimm_pmu to access
0292      * device data in events functions.
0293      */
0294     nd_pmu->dev = &pdev->dev;
0295 
0296     /* Fill attribute groups for the nvdimm pmu device */
0297     nd_pmu->pmu.attr_groups[NVDIMM_PMU_FORMAT_ATTR] = &nvdimm_pmu_format_group;
0298     nd_pmu->pmu.attr_groups[NVDIMM_PMU_EVENT_ATTR] = &nvdimm_pmu_events_group;
0299     nd_pmu->pmu.attr_groups[NVDIMM_PMU_NULL_ATTR] = NULL;
0300 
0301     /* Fill attribute group for cpumask */
0302     rc = nvdimm_pmu_cpu_hotplug_init(nd_pmu);
0303     if (rc) {
0304         pr_info("cpu hotplug feature failed for device: %s\n", nd_pmu->pmu.name);
0305         kfree(nd_pmu->pmu.attr_groups);
0306         return rc;
0307     }
0308 
0309     rc = perf_pmu_register(&nd_pmu->pmu, nd_pmu->pmu.name, -1);
0310     if (rc) {
0311         kfree(nd_pmu->pmu.attr_groups);
0312         nvdimm_pmu_free_hotplug_memory(nd_pmu);
0313         return rc;
0314     }
0315 
0316     pr_info("%s NVDIMM performance monitor support registered\n",
0317         nd_pmu->pmu.name);
0318 
0319     return 0;
0320 }
0321 EXPORT_SYMBOL_GPL(register_nvdimm_pmu);
0322 
0323 void unregister_nvdimm_pmu(struct nvdimm_pmu *nd_pmu)
0324 {
0325     perf_pmu_unregister(&nd_pmu->pmu);
0326     nvdimm_pmu_free_hotplug_memory(nd_pmu);
0327     kfree(nd_pmu);
0328 }
0329 EXPORT_SYMBOL_GPL(unregister_nvdimm_pmu);