0001
0002
0003
0004
0005
0006
0007
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
0019
0020
0021
0022 EVENT(CTL_RES_CNT, 0x1);
0023
0024 EVENT(CTL_RES_TM, 0x2);
0025
0026 EVENT(POWERON_SECS, 0x3);
0027
0028 EVENT(MEM_LIFE, 0x4);
0029
0030 EVENT(CRI_RES_UTIL, 0x5);
0031
0032 EVENT(HOST_L_CNT, 0x6);
0033
0034 EVENT(HOST_S_CNT, 0x7);
0035
0036 EVENT(HOST_S_DUR, 0x8);
0037
0038 EVENT(HOST_L_DUR, 0x9);
0039
0040 EVENT(MED_R_CNT, 0xa);
0041
0042 EVENT(MED_W_CNT, 0xb);
0043
0044 EVENT(MED_R_DUR, 0xc);
0045
0046 EVENT(MED_W_DUR, 0xd);
0047
0048 EVENT(CACHE_RH_CNT, 0xe);
0049
0050 EVENT(CACHE_WH_CNT, 0xf);
0051
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
0139 cpumask_test_and_clear_cpu(cpu, &nd_pmu->arch_cpumask);
0140
0141
0142
0143
0144
0145 if (cpu != nd_pmu->cpu)
0146 return 0;
0147
0148
0149 target = cpumask_any(&nd_pmu->arch_cpumask);
0150
0151
0152
0153
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
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
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
0224
0225
0226
0227
0228 if (!cpumask_empty(&nd_pmu->arch_cpumask)) {
0229 nd_pmu->cpu = cpumask_any(&nd_pmu->arch_cpumask);
0230 } else {
0231
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
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
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
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
0292
0293
0294 nd_pmu->dev = &pdev->dev;
0295
0296
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
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);