diff options
author | Yan, Zheng <zheng.z.yan@intel.com> | 2012-06-15 02:31:36 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-06-18 06:13:23 -0400 |
commit | 14371cce03c2fc393997e17f979e76674b7f392a (patch) | |
tree | 9f062dd2104df744e77da17054ad265e8c236b0c | |
parent | fcde10e916326545e8fec1807357c68ef08dc443 (diff) |
perf: Add generic PCI uncore PMU device support
This patch adds generic support for uncore PMUs presented as
PCI devices. (These come in addition to the CPU/MSR based
uncores.)
Signed-off-by: Zheng Yan <zheng.z.yan@intel.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1339741902-8449-8-git-send-email-zheng.z.yan@intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_uncore.c | 175 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_uncore.h | 66 |
2 files changed, 236 insertions, 5 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index 3ed941ac3745..e20c65a0e108 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c | |||
@@ -2,6 +2,11 @@ | |||
2 | 2 | ||
3 | static struct intel_uncore_type *empty_uncore[] = { NULL, }; | 3 | static struct intel_uncore_type *empty_uncore[] = { NULL, }; |
4 | static struct intel_uncore_type **msr_uncores = empty_uncore; | 4 | static struct intel_uncore_type **msr_uncores = empty_uncore; |
5 | static struct intel_uncore_type **pci_uncores = empty_uncore; | ||
6 | /* pci bus to socket mapping */ | ||
7 | static int pcibus_to_physid[256] = { [0 ... 255] = -1, }; | ||
8 | |||
9 | static DEFINE_RAW_SPINLOCK(uncore_box_lock); | ||
5 | 10 | ||
6 | /* mask of cpus that collect uncore events */ | 11 | /* mask of cpus that collect uncore events */ |
7 | static cpumask_t uncore_cpu_mask; | 12 | static cpumask_t uncore_cpu_mask; |
@@ -205,13 +210,13 @@ static void uncore_assign_hw_event(struct intel_uncore_box *box, | |||
205 | hwc->last_tag = ++box->tags[idx]; | 210 | hwc->last_tag = ++box->tags[idx]; |
206 | 211 | ||
207 | if (hwc->idx == UNCORE_PMC_IDX_FIXED) { | 212 | if (hwc->idx == UNCORE_PMC_IDX_FIXED) { |
208 | hwc->event_base = uncore_msr_fixed_ctr(box); | 213 | hwc->event_base = uncore_fixed_ctr(box); |
209 | hwc->config_base = uncore_msr_fixed_ctl(box); | 214 | hwc->config_base = uncore_fixed_ctl(box); |
210 | return; | 215 | return; |
211 | } | 216 | } |
212 | 217 | ||
213 | hwc->config_base = uncore_msr_event_ctl(box, hwc->idx); | 218 | hwc->config_base = uncore_event_ctl(box, hwc->idx); |
214 | hwc->event_base = uncore_msr_perf_ctr(box, hwc->idx); | 219 | hwc->event_base = uncore_perf_ctr(box, hwc->idx); |
215 | } | 220 | } |
216 | 221 | ||
217 | static void uncore_perf_event_update(struct intel_uncore_box *box, | 222 | static void uncore_perf_event_update(struct intel_uncore_box *box, |
@@ -305,6 +310,22 @@ struct intel_uncore_box *uncore_alloc_box(int cpu) | |||
305 | static struct intel_uncore_box * | 310 | static struct intel_uncore_box * |
306 | uncore_pmu_to_box(struct intel_uncore_pmu *pmu, int cpu) | 311 | uncore_pmu_to_box(struct intel_uncore_pmu *pmu, int cpu) |
307 | { | 312 | { |
313 | static struct intel_uncore_box *box; | ||
314 | |||
315 | box = *per_cpu_ptr(pmu->box, cpu); | ||
316 | if (box) | ||
317 | return box; | ||
318 | |||
319 | raw_spin_lock(&uncore_box_lock); | ||
320 | list_for_each_entry(box, &pmu->box_list, list) { | ||
321 | if (box->phys_id == topology_physical_package_id(cpu)) { | ||
322 | atomic_inc(&box->refcnt); | ||
323 | *per_cpu_ptr(pmu->box, cpu) = box; | ||
324 | break; | ||
325 | } | ||
326 | } | ||
327 | raw_spin_unlock(&uncore_box_lock); | ||
328 | |||
308 | return *per_cpu_ptr(pmu->box, cpu); | 329 | return *per_cpu_ptr(pmu->box, cpu); |
309 | } | 330 | } |
310 | 331 | ||
@@ -706,6 +727,13 @@ static void __init uncore_type_exit(struct intel_uncore_type *type) | |||
706 | type->attr_groups[1] = NULL; | 727 | type->attr_groups[1] = NULL; |
707 | } | 728 | } |
708 | 729 | ||
730 | static void uncore_types_exit(struct intel_uncore_type **types) | ||
731 | { | ||
732 | int i; | ||
733 | for (i = 0; types[i]; i++) | ||
734 | uncore_type_exit(types[i]); | ||
735 | } | ||
736 | |||
709 | static int __init uncore_type_init(struct intel_uncore_type *type) | 737 | static int __init uncore_type_init(struct intel_uncore_type *type) |
710 | { | 738 | { |
711 | struct intel_uncore_pmu *pmus; | 739 | struct intel_uncore_pmu *pmus; |
@@ -725,6 +753,7 @@ static int __init uncore_type_init(struct intel_uncore_type *type) | |||
725 | pmus[i].func_id = -1; | 753 | pmus[i].func_id = -1; |
726 | pmus[i].pmu_idx = i; | 754 | pmus[i].pmu_idx = i; |
727 | pmus[i].type = type; | 755 | pmus[i].type = type; |
756 | INIT_LIST_HEAD(&pmus[i].box_list); | ||
728 | pmus[i].box = alloc_percpu(struct intel_uncore_box *); | 757 | pmus[i].box = alloc_percpu(struct intel_uncore_box *); |
729 | if (!pmus[i].box) | 758 | if (!pmus[i].box) |
730 | goto fail; | 759 | goto fail; |
@@ -773,6 +802,127 @@ fail: | |||
773 | return ret; | 802 | return ret; |
774 | } | 803 | } |
775 | 804 | ||
805 | static struct pci_driver *uncore_pci_driver; | ||
806 | static bool pcidrv_registered; | ||
807 | |||
808 | /* | ||
809 | * add a pci uncore device | ||
810 | */ | ||
811 | static int __devinit uncore_pci_add(struct intel_uncore_type *type, | ||
812 | struct pci_dev *pdev) | ||
813 | { | ||
814 | struct intel_uncore_pmu *pmu; | ||
815 | struct intel_uncore_box *box; | ||
816 | int i, phys_id; | ||
817 | |||
818 | phys_id = pcibus_to_physid[pdev->bus->number]; | ||
819 | if (phys_id < 0) | ||
820 | return -ENODEV; | ||
821 | |||
822 | box = uncore_alloc_box(0); | ||
823 | if (!box) | ||
824 | return -ENOMEM; | ||
825 | |||
826 | /* | ||
827 | * for performance monitoring unit with multiple boxes, | ||
828 | * each box has a different function id. | ||
829 | */ | ||
830 | for (i = 0; i < type->num_boxes; i++) { | ||
831 | pmu = &type->pmus[i]; | ||
832 | if (pmu->func_id == pdev->devfn) | ||
833 | break; | ||
834 | if (pmu->func_id < 0) { | ||
835 | pmu->func_id = pdev->devfn; | ||
836 | break; | ||
837 | } | ||
838 | pmu = NULL; | ||
839 | } | ||
840 | |||
841 | if (!pmu) { | ||
842 | kfree(box); | ||
843 | return -EINVAL; | ||
844 | } | ||
845 | |||
846 | box->phys_id = phys_id; | ||
847 | box->pci_dev = pdev; | ||
848 | box->pmu = pmu; | ||
849 | uncore_box_init(box); | ||
850 | pci_set_drvdata(pdev, box); | ||
851 | |||
852 | raw_spin_lock(&uncore_box_lock); | ||
853 | list_add_tail(&box->list, &pmu->box_list); | ||
854 | raw_spin_unlock(&uncore_box_lock); | ||
855 | |||
856 | return 0; | ||
857 | } | ||
858 | |||
859 | static void __devexit uncore_pci_remove(struct pci_dev *pdev) | ||
860 | { | ||
861 | struct intel_uncore_box *box = pci_get_drvdata(pdev); | ||
862 | struct intel_uncore_pmu *pmu = box->pmu; | ||
863 | int cpu, phys_id = pcibus_to_physid[pdev->bus->number]; | ||
864 | |||
865 | if (WARN_ON_ONCE(phys_id != box->phys_id)) | ||
866 | return; | ||
867 | |||
868 | raw_spin_lock(&uncore_box_lock); | ||
869 | list_del(&box->list); | ||
870 | raw_spin_unlock(&uncore_box_lock); | ||
871 | |||
872 | for_each_possible_cpu(cpu) { | ||
873 | if (*per_cpu_ptr(pmu->box, cpu) == box) { | ||
874 | *per_cpu_ptr(pmu->box, cpu) = NULL; | ||
875 | atomic_dec(&box->refcnt); | ||
876 | } | ||
877 | } | ||
878 | |||
879 | WARN_ON_ONCE(atomic_read(&box->refcnt) != 1); | ||
880 | kfree(box); | ||
881 | } | ||
882 | |||
883 | static int __devinit uncore_pci_probe(struct pci_dev *pdev, | ||
884 | const struct pci_device_id *id) | ||
885 | { | ||
886 | struct intel_uncore_type *type; | ||
887 | |||
888 | type = (struct intel_uncore_type *)id->driver_data; | ||
889 | return uncore_pci_add(type, pdev); | ||
890 | } | ||
891 | |||
892 | static int __init uncore_pci_init(void) | ||
893 | { | ||
894 | int ret; | ||
895 | |||
896 | switch (boot_cpu_data.x86_model) { | ||
897 | default: | ||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | ret = uncore_types_init(pci_uncores); | ||
902 | if (ret) | ||
903 | return ret; | ||
904 | |||
905 | uncore_pci_driver->probe = uncore_pci_probe; | ||
906 | uncore_pci_driver->remove = uncore_pci_remove; | ||
907 | |||
908 | ret = pci_register_driver(uncore_pci_driver); | ||
909 | if (ret == 0) | ||
910 | pcidrv_registered = true; | ||
911 | else | ||
912 | uncore_types_exit(pci_uncores); | ||
913 | |||
914 | return ret; | ||
915 | } | ||
916 | |||
917 | static void __init uncore_pci_exit(void) | ||
918 | { | ||
919 | if (pcidrv_registered) { | ||
920 | pcidrv_registered = false; | ||
921 | pci_unregister_driver(uncore_pci_driver); | ||
922 | uncore_types_exit(pci_uncores); | ||
923 | } | ||
924 | } | ||
925 | |||
776 | static void __cpuinit uncore_cpu_dying(int cpu) | 926 | static void __cpuinit uncore_cpu_dying(int cpu) |
777 | { | 927 | { |
778 | struct intel_uncore_type *type; | 928 | struct intel_uncore_type *type; |
@@ -921,6 +1071,7 @@ static void __cpuinit uncore_event_exit_cpu(int cpu) | |||
921 | cpumask_set_cpu(target, &uncore_cpu_mask); | 1071 | cpumask_set_cpu(target, &uncore_cpu_mask); |
922 | 1072 | ||
923 | uncore_change_context(msr_uncores, cpu, target); | 1073 | uncore_change_context(msr_uncores, cpu, target); |
1074 | uncore_change_context(pci_uncores, cpu, target); | ||
924 | } | 1075 | } |
925 | 1076 | ||
926 | static void __cpuinit uncore_event_init_cpu(int cpu) | 1077 | static void __cpuinit uncore_event_init_cpu(int cpu) |
@@ -936,6 +1087,7 @@ static void __cpuinit uncore_event_init_cpu(int cpu) | |||
936 | cpumask_set_cpu(cpu, &uncore_cpu_mask); | 1087 | cpumask_set_cpu(cpu, &uncore_cpu_mask); |
937 | 1088 | ||
938 | uncore_change_context(msr_uncores, -1, cpu); | 1089 | uncore_change_context(msr_uncores, -1, cpu); |
1090 | uncore_change_context(pci_uncores, -1, cpu); | ||
939 | } | 1091 | } |
940 | 1092 | ||
941 | static int __cpuinit uncore_cpu_notifier(struct notifier_block *self, | 1093 | static int __cpuinit uncore_cpu_notifier(struct notifier_block *self, |
@@ -1051,6 +1203,14 @@ static int __init uncore_pmus_register(void) | |||
1051 | } | 1203 | } |
1052 | } | 1204 | } |
1053 | 1205 | ||
1206 | for (i = 0; pci_uncores[i]; i++) { | ||
1207 | type = pci_uncores[i]; | ||
1208 | for (j = 0; j < type->num_boxes; j++) { | ||
1209 | pmu = &type->pmus[j]; | ||
1210 | uncore_pmu_register(pmu); | ||
1211 | } | ||
1212 | } | ||
1213 | |||
1054 | return 0; | 1214 | return 0; |
1055 | } | 1215 | } |
1056 | 1216 | ||
@@ -1061,9 +1221,14 @@ static int __init intel_uncore_init(void) | |||
1061 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) | 1221 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) |
1062 | return -ENODEV; | 1222 | return -ENODEV; |
1063 | 1223 | ||
1064 | ret = uncore_cpu_init(); | 1224 | ret = uncore_pci_init(); |
1065 | if (ret) | 1225 | if (ret) |
1066 | goto fail; | 1226 | goto fail; |
1227 | ret = uncore_cpu_init(); | ||
1228 | if (ret) { | ||
1229 | uncore_pci_exit(); | ||
1230 | goto fail; | ||
1231 | } | ||
1067 | 1232 | ||
1068 | uncore_pmus_register(); | 1233 | uncore_pmus_register(); |
1069 | return 0; | 1234 | return 0; |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h index eeb5ca5815a8..aa01df87b8de 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <linux/module.h> | 1 | #include <linux/module.h> |
2 | #include <linux/slab.h> | 2 | #include <linux/slab.h> |
3 | #include <linux/pci.h> | ||
3 | #include <linux/perf_event.h> | 4 | #include <linux/perf_event.h> |
4 | #include "perf_event.h" | 5 | #include "perf_event.h" |
5 | 6 | ||
@@ -110,6 +111,7 @@ struct intel_uncore_pmu { | |||
110 | int func_id; | 111 | int func_id; |
111 | struct intel_uncore_type *type; | 112 | struct intel_uncore_type *type; |
112 | struct intel_uncore_box ** __percpu box; | 113 | struct intel_uncore_box ** __percpu box; |
114 | struct list_head box_list; | ||
113 | }; | 115 | }; |
114 | 116 | ||
115 | struct intel_uncore_box { | 117 | struct intel_uncore_box { |
@@ -123,6 +125,7 @@ struct intel_uncore_box { | |||
123 | struct perf_event *event_list[UNCORE_PMC_IDX_MAX]; | 125 | struct perf_event *event_list[UNCORE_PMC_IDX_MAX]; |
124 | unsigned long active_mask[BITS_TO_LONGS(UNCORE_PMC_IDX_MAX)]; | 126 | unsigned long active_mask[BITS_TO_LONGS(UNCORE_PMC_IDX_MAX)]; |
125 | u64 tags[UNCORE_PMC_IDX_MAX]; | 127 | u64 tags[UNCORE_PMC_IDX_MAX]; |
128 | struct pci_dev *pci_dev; | ||
126 | struct intel_uncore_pmu *pmu; | 129 | struct intel_uncore_pmu *pmu; |
127 | struct hrtimer hrtimer; | 130 | struct hrtimer hrtimer; |
128 | struct list_head list; | 131 | struct list_head list; |
@@ -161,6 +164,33 @@ static ssize_t uncore_event_show(struct kobject *kobj, | |||
161 | return sprintf(buf, "%s", event->config); | 164 | return sprintf(buf, "%s", event->config); |
162 | } | 165 | } |
163 | 166 | ||
167 | static inline unsigned uncore_pci_box_ctl(struct intel_uncore_box *box) | ||
168 | { | ||
169 | return box->pmu->type->box_ctl; | ||
170 | } | ||
171 | |||
172 | static inline unsigned uncore_pci_fixed_ctl(struct intel_uncore_box *box) | ||
173 | { | ||
174 | return box->pmu->type->fixed_ctl; | ||
175 | } | ||
176 | |||
177 | static inline unsigned uncore_pci_fixed_ctr(struct intel_uncore_box *box) | ||
178 | { | ||
179 | return box->pmu->type->fixed_ctr; | ||
180 | } | ||
181 | |||
182 | static inline | ||
183 | unsigned uncore_pci_event_ctl(struct intel_uncore_box *box, int idx) | ||
184 | { | ||
185 | return idx * 4 + box->pmu->type->event_ctl; | ||
186 | } | ||
187 | |||
188 | static inline | ||
189 | unsigned uncore_pci_perf_ctr(struct intel_uncore_box *box, int idx) | ||
190 | { | ||
191 | return idx * 8 + box->pmu->type->perf_ctr; | ||
192 | } | ||
193 | |||
164 | static inline | 194 | static inline |
165 | unsigned uncore_msr_box_ctl(struct intel_uncore_box *box) | 195 | unsigned uncore_msr_box_ctl(struct intel_uncore_box *box) |
166 | { | 196 | { |
@@ -200,6 +230,42 @@ unsigned uncore_msr_perf_ctr(struct intel_uncore_box *box, int idx) | |||
200 | box->pmu->type->msr_offset * box->pmu->pmu_idx; | 230 | box->pmu->type->msr_offset * box->pmu->pmu_idx; |
201 | } | 231 | } |
202 | 232 | ||
233 | static inline | ||
234 | unsigned uncore_fixed_ctl(struct intel_uncore_box *box) | ||
235 | { | ||
236 | if (box->pci_dev) | ||
237 | return uncore_pci_fixed_ctl(box); | ||
238 | else | ||
239 | return uncore_msr_fixed_ctl(box); | ||
240 | } | ||
241 | |||
242 | static inline | ||
243 | unsigned uncore_fixed_ctr(struct intel_uncore_box *box) | ||
244 | { | ||
245 | if (box->pci_dev) | ||
246 | return uncore_pci_fixed_ctr(box); | ||
247 | else | ||
248 | return uncore_msr_fixed_ctr(box); | ||
249 | } | ||
250 | |||
251 | static inline | ||
252 | unsigned uncore_event_ctl(struct intel_uncore_box *box, int idx) | ||
253 | { | ||
254 | if (box->pci_dev) | ||
255 | return uncore_pci_event_ctl(box, idx); | ||
256 | else | ||
257 | return uncore_msr_event_ctl(box, idx); | ||
258 | } | ||
259 | |||
260 | static inline | ||
261 | unsigned uncore_perf_ctr(struct intel_uncore_box *box, int idx) | ||
262 | { | ||
263 | if (box->pci_dev) | ||
264 | return uncore_pci_perf_ctr(box, idx); | ||
265 | else | ||
266 | return uncore_msr_perf_ctr(box, idx); | ||
267 | } | ||
268 | |||
203 | static inline int uncore_perf_ctr_bits(struct intel_uncore_box *box) | 269 | static inline int uncore_perf_ctr_bits(struct intel_uncore_box *box) |
204 | { | 270 | { |
205 | return box->pmu->type->perf_ctr_bits; | 271 | return box->pmu->type->perf_ctr_bits; |