author | kucharsk |
Thu, 27 Oct 2005 14:59:45 -0700 | |
changeset 770 | 0eda482eb80f |
parent 0 | 68f95e015346 |
child 948 | cd52fd15e97d |
permissions | -rw-r--r-- |
0 | 1 |
/* |
2 |
* CDDL HEADER START |
|
3 |
* |
|
4 |
* The contents of this file are subject to the terms of the |
|
5 |
* Common Development and Distribution License, Version 1.0 only |
|
6 |
* (the "License"). You may not use this file except in compliance |
|
7 |
* with the License. |
|
8 |
* |
|
9 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
10 |
* or http://www.opensolaris.org/os/licensing. |
|
11 |
* See the License for the specific language governing permissions |
|
12 |
* and limitations under the License. |
|
13 |
* |
|
14 |
* When distributing Covered Code, include this CDDL HEADER in each |
|
15 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
16 |
* If applicable, add the following below this CDDL HEADER, with the |
|
17 |
* fields enclosed by brackets "[]" replaced with your own identifying |
|
18 |
* information: Portions Copyright [yyyy] [name of copyright owner] |
|
19 |
* |
|
20 |
* CDDL HEADER END |
|
21 |
*/ |
|
22 |
/* |
|
770
0eda482eb80f
6311933 rdmsr/wrmsr do not need to set/pass values via memory pointers
kucharsk
parents:
0
diff
changeset
|
23 |
* Copyright 2005 Sun Microsystems, Inc. All rights reserved. |
0 | 24 |
* Use is subject to license terms. |
25 |
*/ |
|
26 |
||
27 |
#pragma ident "%Z%%M% %I% %E% SMI" |
|
28 |
||
29 |
/* |
|
30 |
* Performance Counter Back-End for AMD Opteron and AMD Athlon 64 processors. |
|
31 |
*/ |
|
32 |
||
33 |
#include <sys/cpuvar.h> |
|
34 |
#include <sys/param.h> |
|
35 |
#include <sys/systm.h> |
|
36 |
#include <sys/cpc_pcbe.h> |
|
37 |
#include <sys/kmem.h> |
|
38 |
#include <sys/sdt.h> |
|
39 |
#include <sys/modctl.h> |
|
40 |
#include <sys/errno.h> |
|
41 |
#include <sys/debug.h> |
|
42 |
#include <sys/archsystm.h> |
|
43 |
#include <sys/x86_archext.h> |
|
44 |
#include <sys/privregs.h> |
|
45 |
||
46 |
static int opt_pcbe_init(void); |
|
47 |
static uint_t opt_pcbe_ncounters(void); |
|
48 |
static const char *opt_pcbe_impl_name(void); |
|
49 |
static const char *opt_pcbe_cpuref(void); |
|
50 |
static char *opt_pcbe_list_events(uint_t picnum); |
|
51 |
static char *opt_pcbe_list_attrs(void); |
|
52 |
static uint64_t opt_pcbe_event_coverage(char *event); |
|
53 |
static uint64_t opt_pcbe_overflow_bitmap(void); |
|
54 |
static int opt_pcbe_configure(uint_t picnum, char *event, uint64_t preset, |
|
55 |
uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data, |
|
56 |
void *token); |
|
57 |
static void opt_pcbe_program(void *token); |
|
58 |
static void opt_pcbe_allstop(void); |
|
59 |
static void opt_pcbe_sample(void *token); |
|
60 |
static void opt_pcbe_free(void *config); |
|
61 |
||
62 |
static pcbe_ops_t opt_pcbe_ops = { |
|
63 |
PCBE_VER_1, |
|
64 |
CPC_CAP_OVERFLOW_INTERRUPT, |
|
65 |
opt_pcbe_ncounters, |
|
66 |
opt_pcbe_impl_name, |
|
67 |
opt_pcbe_cpuref, |
|
68 |
opt_pcbe_list_events, |
|
69 |
opt_pcbe_list_attrs, |
|
70 |
opt_pcbe_event_coverage, |
|
71 |
opt_pcbe_overflow_bitmap, |
|
72 |
opt_pcbe_configure, |
|
73 |
opt_pcbe_program, |
|
74 |
opt_pcbe_allstop, |
|
75 |
opt_pcbe_sample, |
|
76 |
opt_pcbe_free |
|
77 |
}; |
|
78 |
||
79 |
/* |
|
80 |
* Define offsets and masks for the fields in the Performance |
|
81 |
* Event-Select (PES) registers. |
|
82 |
*/ |
|
83 |
#define OPT_PES_CMASK_SHIFT 24 |
|
84 |
#define OPT_PES_CMASK_MASK 0xFF |
|
85 |
#define OPT_PES_INV_SHIFT 23 |
|
86 |
#define OPT_PES_ENABLE_SHIFT 22 |
|
87 |
#define OPT_PES_INT_SHIFT 20 |
|
88 |
#define OPT_PES_PC_SHIFT 19 |
|
89 |
#define OPT_PES_EDGE_SHIFT 18 |
|
90 |
#define OPT_PES_OS_SHIFT 17 |
|
91 |
#define OPT_PES_USR_SHIFT 16 |
|
92 |
#define OPT_PES_UMASK_SHIFT 8 |
|
93 |
#define OPT_PES_UMASK_MASK 0xFF |
|
94 |
||
95 |
#define OPT_PES_INV (1 << OPT_PES_INV_SHIFT) |
|
96 |
#define OPT_PES_ENABLE (1 << OPT_PES_ENABLE_SHIFT) |
|
97 |
#define OPT_PES_INT (1 << OPT_PES_INT_SHIFT) |
|
98 |
#define OPT_PES_PC (1 << OPT_PES_PC_SHIFT) |
|
99 |
#define OPT_PES_EDGE (1 << OPT_PES_EDGE_SHIFT) |
|
100 |
#define OPT_PES_OS (1 << OPT_PES_OS_SHIFT) |
|
101 |
#define OPT_PES_USR (1 << OPT_PES_USR_SHIFT) |
|
102 |
||
103 |
typedef struct _opt_pcbe_config { |
|
104 |
uint8_t opt_picno; /* Counter number: 0, 1, 2, or 3 */ |
|
105 |
uint64_t opt_evsel; /* Event Selection register */ |
|
106 |
uint64_t opt_rawpic; /* Raw counter value */ |
|
107 |
} opt_pcbe_config_t; |
|
108 |
||
109 |
opt_pcbe_config_t nullcfgs[4] = { |
|
110 |
{ 0, 0, 0 }, |
|
111 |
{ 1, 0, 0 }, |
|
112 |
{ 2, 0, 0 }, |
|
113 |
{ 3, 0, 0 } |
|
114 |
}; |
|
115 |
||
116 |
typedef struct _opt_event { |
|
117 |
char *name; |
|
118 |
uint8_t emask; /* Event mask setting */ |
|
119 |
uint8_t umask_valid; /* Mask of unreserved UNIT_MASK bits */ |
|
120 |
} opt_event_t; |
|
121 |
||
122 |
/* |
|
123 |
* Base MSR addresses for the PerfEvtSel registers and the counters themselves. |
|
124 |
* Add counter number to base address to get corresponding MSR address. |
|
125 |
*/ |
|
126 |
#define PES_BASE_ADDR 0xC0010000 |
|
127 |
#define PIC_BASE_ADDR 0xC0010004 |
|
128 |
||
129 |
#define MASK48 0xFFFFFFFFFFFF |
|
130 |
||
131 |
#define EV_END {NULL, 0, 0} |
|
132 |
||
133 |
static opt_event_t opt_events[] = { |
|
134 |
{ "FP_dispatched_fpu_ops", 0x0, 0x1F }, |
|
135 |
{ "FP_cycles_no_fpu_ops_retired", 0x1, 0x0 }, |
|
136 |
{ "FP_dispatched_fpu_ops_ff", 0x2, 0x0 }, |
|
137 |
{ "LS_seg_reg_load", 0x20, 0x7F }, |
|
138 |
{ "LS_uarch_resync_self_modify", 0x21, 0x0 }, |
|
139 |
{ "LS_uarch_resync_snoop", 0x22, 0x0 }, |
|
140 |
{ "LS_buffer_2_full", 0x23, 0x0 }, |
|
141 |
{ "LS_locked_operation", 0x24, 0x7 }, |
|
142 |
{ "LS_uarch_late_cancel_op", 0x25, 0x0 }, |
|
143 |
{ "LS_retired_cflush", 0x26, 0x0 }, |
|
144 |
{ "LS_retired_cpuid", 0x27, 0x0 }, |
|
145 |
{ "DC_access", 0x40, 0x0 }, |
|
146 |
{ "DC_miss", 0x41, 0x0 }, |
|
147 |
{ "DC_refill_from_L2", 0x42, 0x1F }, |
|
148 |
{ "DC_refill_from_system", 0x43, 0x1F }, |
|
149 |
{ "DC_copyback", 0x44, 0x1F }, |
|
150 |
{ "DC_dtlb_L1_miss_L2_hit", 0x45, 0x0 }, |
|
151 |
{ "DC_dtlb_L1_miss_L2_miss", 0x46, 0x0 }, |
|
152 |
{ "DC_misaligned_data_ref", 0x47, 0x0 }, |
|
153 |
{ "DC_uarch_late_cancel_access", 0x48, 0x0 }, |
|
154 |
{ "DC_uarch_early_cancel_access", 0x49, 0x0 }, |
|
155 |
{ "DC_1bit_ecc_error_found", 0x4A, 0x3 }, |
|
156 |
{ "DC_dispatched_prefetch_instr", 0x4B, 0x7 }, |
|
157 |
{ "DC_dcache_accesses_by_locks", 0x4C, 0x3 }, |
|
158 |
{ "BU_cpu_clk_unhalted", 0x76, 0x0 }, |
|
159 |
{ "BU_internal_L2_req", 0x7D, 0x1F }, |
|
160 |
{ "BU_fill_req_missed_L2", 0x7E, 0x7 }, |
|
161 |
{ "BU_fill_into_L2", 0x7F, 0x3 }, |
|
162 |
{ "IC_fetch", 0x80, 0x0 }, |
|
163 |
{ "IC_miss", 0x81, 0x0 }, |
|
164 |
{ "IC_refill_from_L2", 0x82, 0x0 }, |
|
165 |
{ "IC_refill_from_system", 0x83, 0x0 }, |
|
166 |
{ "IC_itlb_L1_miss_L2_hit", 0x84, 0x0 }, |
|
167 |
{ "IC_itlb_L1_miss_L2_miss", 0x85, 0x0 }, |
|
168 |
{ "IC_uarch_resync_snoop", 0x86, 0x0 }, |
|
169 |
{ "IC_instr_fetch_stall", 0x87, 0x0 }, |
|
170 |
{ "IC_return_stack_hit", 0x88, 0x0 }, |
|
171 |
{ "IC_return_stack_overflow", 0x89, 0x0 }, |
|
172 |
{ "FR_retired_x86_instr_w_excp_intr", 0xC0, 0x0 }, |
|
173 |
{ "FR_retired_uops", 0xC1, 0x0 }, |
|
174 |
{ "FR_retired_branches_w_excp_intr", 0xC2, 0x0 }, |
|
175 |
{ "FR_retired_branches_mispred", 0xC3, 0x0 }, |
|
176 |
{ "FR_retired_taken_branches", 0xC4, 0x0 }, |
|
177 |
{ "FR_retired_taken_branches_mispred", 0xC5, 0x0 }, |
|
178 |
{ "FR_retired_far_ctl_transfer", 0xC6, 0x0 }, |
|
179 |
{ "FR_retired_resyncs", 0xC7, 0x0 }, |
|
180 |
{ "FR_retired_near_rets", 0xC8, 0x0 }, |
|
181 |
{ "FR_retired_near_rets_mispred", 0xC9, 0x0 }, |
|
182 |
{ "FR_retired_taken_branches_mispred_addr_miscomp", 0xCA, 0x0 }, |
|
183 |
{ "FR_retired_fpu_instr", 0xCB, 0xF }, |
|
184 |
{ "FR_retired_fastpath_double_op_instr", 0xCC, 0x7 }, |
|
185 |
{ "FR_intr_masked_cycles", 0xCD, 0x0 }, |
|
186 |
{ "FR_intr_masked_while_pending_cycles", 0xCE, 0x0 }, |
|
187 |
{ "FR_taken_hardware_intrs", 0xCF, 0x0 }, |
|
188 |
{ "FR_nothing_to_dispatch", 0xD0, 0x0 }, |
|
189 |
{ "FR_dispatch_stalls", 0xD1, 0x0 }, |
|
190 |
{ "FR_dispatch_stall_branch_abort_to_retire", 0xD2, 0x0 }, |
|
191 |
{ "FR_dispatch_stall_serialization", 0xD3, 0x0 }, |
|
192 |
{ "FR_dispatch_stall_segment_load", 0xD4, 0x0 }, |
|
193 |
{ "FR_dispatch_stall_reorder_buffer_full", 0xD5, 0x0 }, |
|
194 |
{ "FR_dispatch_stall_resv_stations_full", 0xD6, 0x0 }, |
|
195 |
{ "FR_dispatch_stall_fpu_full", 0xD7, 0x0 }, |
|
196 |
{ "FR_dispatch_stall_ls_full", 0xD8, 0x0 }, |
|
197 |
{ "FR_dispatch_stall_waiting_all_quiet", 0xD9, 0x0 }, |
|
198 |
{ "FR_dispatch_stall_far_ctl_trsfr_resync_branch_pend", 0xDA, 0x0 }, |
|
199 |
{ "FR_fpu_exception", 0xDB, 0xF }, |
|
200 |
{ "FR_num_brkpts_dr0", 0xDC, 0x0 }, |
|
201 |
{ "FR_num_brkpts_dr1", 0xDD, 0x0 }, |
|
202 |
{ "FR_num_brkpts_dr2", 0xDE, 0x0 }, |
|
203 |
{ "FR_num_brkpts_dr3", 0xDF, 0x0 }, |
|
204 |
{ "NB_mem_ctrlr_page_access", 0xE0, 0x7 }, |
|
205 |
{ "NB_mem_ctrlr_page_table_overflow", 0xE1, 0x0 }, |
|
206 |
{ "NB_mem_ctrlr_dram_cmd_slots_missed", 0xE2, 0x0 }, |
|
207 |
{ "NB_mem_ctrlr_turnaround", 0xE3, 0x7 }, |
|
208 |
{ "NB_mem_ctrlr_bypass_counter_saturation", 0xE4, 0xF }, |
|
209 |
{ "NB_sized_commands", 0xEB, 0x7F }, |
|
210 |
{ "NB_probe_result", 0xEC, 0xF }, |
|
211 |
{ "NB_ht_bus0_bandwidth", 0xF6, 0xF }, |
|
212 |
{ "NB_ht_bus1_bandwidth", 0xF7, 0xF }, |
|
213 |
{ "NB_ht_bus2_bandwidth", 0xF8, 0xF }, |
|
214 |
EV_END |
|
215 |
}; |
|
216 |
||
217 |
static char *evlist; |
|
218 |
static size_t evlist_sz; |
|
219 |
||
220 |
#define BITS(v, u, l) \ |
|
221 |
(((v) >> (l)) & ((1 << (1 + (u) - (l))) - 1)) |
|
222 |
||
223 |
#define OPTERON_FAMILY 15 |
|
224 |
||
225 |
static int |
|
226 |
opt_pcbe_init(void) |
|
227 |
{ |
|
228 |
opt_event_t *evp; |
|
229 |
||
230 |
/* |
|
231 |
* Make sure this really _is_ an Opteron or Athlon 64 system. The kernel |
|
232 |
* loads this module based on its name in the module directory, but it |
|
233 |
* could have been renamed. |
|
234 |
*/ |
|
235 |
if (cpuid_getvendor(CPU) != X86_VENDOR_AMD || |
|
236 |
cpuid_getfamily(CPU) != OPTERON_FAMILY) |
|
237 |
return (-1); |
|
238 |
||
239 |
/* |
|
240 |
* Construct event list. |
|
241 |
* |
|
242 |
* First pass: Calculate size needed. We'll need an additional byte |
|
243 |
* for the NULL pointer during the last strcat. |
|
244 |
* |
|
245 |
* Second pass: Copy strings. |
|
246 |
*/ |
|
247 |
for (evp = opt_events; evp->name != NULL; evp++) |
|
248 |
evlist_sz += strlen(evp->name) + 1; |
|
249 |
||
250 |
evlist = kmem_alloc(evlist_sz + 1, KM_SLEEP); |
|
251 |
evlist[0] = '\0'; |
|
252 |
||
253 |
for (evp = opt_events; evp->name != NULL; evp++) { |
|
254 |
(void) strcat(evlist, evp->name); |
|
255 |
(void) strcat(evlist, ","); |
|
256 |
} |
|
257 |
/* |
|
258 |
* Remove trailing comma. |
|
259 |
*/ |
|
260 |
evlist[evlist_sz - 1] = '\0'; |
|
261 |
||
262 |
return (0); |
|
263 |
} |
|
264 |
||
265 |
static uint_t |
|
266 |
opt_pcbe_ncounters(void) |
|
267 |
{ |
|
268 |
return (4); |
|
269 |
} |
|
270 |
||
271 |
static const char * |
|
272 |
opt_pcbe_impl_name(void) |
|
273 |
{ |
|
274 |
return ("AMD Opteron & Athlon64"); |
|
275 |
} |
|
276 |
||
277 |
static const char * |
|
278 |
opt_pcbe_cpuref(void) |
|
279 |
{ |
|
280 |
return ("See Chapter 10 of the \"BIOS and Kernel Developer's Guide " |
|
281 |
"for the AMD Athlon 64 and AMD Opteron Processors,\" " |
|
282 |
"AMD publication #26094"); |
|
283 |
} |
|
284 |
||
285 |
/*ARGSUSED*/ |
|
286 |
static char * |
|
287 |
opt_pcbe_list_events(uint_t picnum) |
|
288 |
{ |
|
289 |
return (evlist); |
|
290 |
} |
|
291 |
||
292 |
static char * |
|
293 |
opt_pcbe_list_attrs(void) |
|
294 |
{ |
|
295 |
return ("edge,pc,inv,cmask,umask"); |
|
296 |
} |
|
297 |
||
298 |
/*ARGSUSED*/ |
|
299 |
static uint64_t |
|
300 |
opt_pcbe_event_coverage(char *event) |
|
301 |
{ |
|
302 |
/* |
|
303 |
* Fortunately, all counters can count all events. |
|
304 |
*/ |
|
305 |
return (0xF); |
|
306 |
} |
|
307 |
||
308 |
static uint64_t |
|
309 |
opt_pcbe_overflow_bitmap(void) |
|
310 |
{ |
|
311 |
/* |
|
312 |
* Unfortunately, this chip cannot detect which counter overflowed, so |
|
313 |
* we must act as if they all did. |
|
314 |
*/ |
|
315 |
return (0xF); |
|
316 |
} |
|
317 |
||
318 |
static opt_event_t * |
|
319 |
find_event(char *name) |
|
320 |
{ |
|
321 |
opt_event_t *evp; |
|
322 |
||
323 |
for (evp = opt_events; evp->name != NULL; evp++) |
|
324 |
if (strcmp(name, evp->name) == 0) |
|
325 |
return (evp); |
|
326 |
||
327 |
return (NULL); |
|
328 |
} |
|
329 |
||
330 |
/*ARGSUSED*/ |
|
331 |
static int |
|
332 |
opt_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags, |
|
333 |
uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token) |
|
334 |
{ |
|
335 |
opt_pcbe_config_t *cfg; |
|
336 |
opt_event_t *evp; |
|
337 |
int i; |
|
338 |
uint32_t evsel = 0; |
|
339 |
||
340 |
/* |
|
341 |
* If we've been handed an existing configuration, we need only preset |
|
342 |
* the counter value. |
|
343 |
*/ |
|
344 |
if (*data != NULL) { |
|
345 |
cfg = *data; |
|
346 |
cfg->opt_rawpic = preset & MASK48; |
|
347 |
return (0); |
|
348 |
} |
|
349 |
||
350 |
if (picnum >= 4) |
|
351 |
return (CPC_INVALID_PICNUM); |
|
352 |
||
353 |
if ((evp = find_event(event)) == NULL) |
|
354 |
return (CPC_INVALID_EVENT); |
|
355 |
||
356 |
evsel |= evp->emask; |
|
357 |
||
358 |
if (flags & CPC_COUNT_USER) |
|
359 |
evsel |= OPT_PES_USR; |
|
360 |
if (flags & CPC_COUNT_SYSTEM) |
|
361 |
evsel |= OPT_PES_OS; |
|
362 |
if (flags & CPC_OVF_NOTIFY_EMT) |
|
363 |
evsel |= OPT_PES_INT; |
|
364 |
||
365 |
for (i = 0; i < nattrs; i++) { |
|
366 |
if (strcmp(attrs[i].ka_name, "edge") == 0) { |
|
367 |
if (attrs[i].ka_val != 0) |
|
368 |
evsel |= OPT_PES_EDGE; |
|
369 |
} else if (strcmp(attrs[i].ka_name, "pc") == 0) { |
|
370 |
if (attrs[i].ka_val != 0) |
|
371 |
evsel |= OPT_PES_PC; |
|
372 |
} else if (strcmp(attrs[i].ka_name, "inv") == 0) { |
|
373 |
if (attrs[i].ka_val != 0) |
|
374 |
evsel |= OPT_PES_INV; |
|
375 |
} else if (strcmp(attrs[i].ka_name, "cmask") == 0) { |
|
376 |
if ((attrs[i].ka_val | OPT_PES_CMASK_MASK) != |
|
377 |
OPT_PES_CMASK_MASK) |
|
378 |
return (CPC_ATTRIBUTE_OUT_OF_RANGE); |
|
379 |
evsel |= attrs[i].ka_val << OPT_PES_CMASK_SHIFT; |
|
380 |
} else if (strcmp(attrs[i].ka_name, "umask") == 0) { |
|
381 |
if ((attrs[i].ka_val | evp->umask_valid) != |
|
382 |
evp->umask_valid) |
|
383 |
return (CPC_ATTRIBUTE_OUT_OF_RANGE); |
|
384 |
evsel |= attrs[i].ka_val << OPT_PES_UMASK_SHIFT; |
|
385 |
} else |
|
386 |
return (CPC_INVALID_ATTRIBUTE); |
|
387 |
} |
|
388 |
||
389 |
cfg = kmem_alloc(sizeof (*cfg), KM_SLEEP); |
|
390 |
||
391 |
cfg->opt_picno = picnum; |
|
392 |
cfg->opt_evsel = evsel; |
|
393 |
cfg->opt_rawpic = preset & MASK48; |
|
394 |
||
395 |
*data = cfg; |
|
396 |
return (0); |
|
397 |
} |
|
398 |
||
399 |
static void |
|
400 |
opt_pcbe_program(void *token) |
|
401 |
{ |
|
402 |
opt_pcbe_config_t *cfgs[4] = { &nullcfgs[0], &nullcfgs[1], |
|
403 |
&nullcfgs[2], &nullcfgs[3] }; |
|
404 |
opt_pcbe_config_t *pcfg = NULL; |
|
405 |
int i; |
|
406 |
uint32_t curcr4 = getcr4(); |
|
407 |
||
408 |
/* |
|
409 |
* Allow nonprivileged code to read the performance counters if desired. |
|
410 |
*/ |
|
411 |
if (kcpc_allow_nonpriv(token)) |
|
412 |
setcr4(curcr4 | CR4_PCE); |
|
413 |
else |
|
414 |
setcr4(curcr4 & ~CR4_PCE); |
|
415 |
||
416 |
/* |
|
417 |
* Query kernel for all configs which will be co-programmed. |
|
418 |
*/ |
|
419 |
do { |
|
420 |
pcfg = (opt_pcbe_config_t *)kcpc_next_config(token, pcfg, NULL); |
|
421 |
||
422 |
if (pcfg != NULL) { |
|
423 |
ASSERT(pcfg->opt_picno < 4); |
|
424 |
cfgs[pcfg->opt_picno] = pcfg; |
|
425 |
} |
|
426 |
} while (pcfg != NULL); |
|
427 |
||
428 |
/* |
|
429 |
* Program in two loops. The first configures and presets the counter, |
|
430 |
* and the second loop enables the counters. This ensures that the |
|
431 |
* counters are all enabled as closely together in time as possible. |
|
432 |
*/ |
|
433 |
||
434 |
for (i = 0; i < 4; i++) { |
|
770
0eda482eb80f
6311933 rdmsr/wrmsr do not need to set/pass values via memory pointers
kucharsk
parents:
0
diff
changeset
|
435 |
wrmsr(PES_BASE_ADDR + i, cfgs[i]->opt_evsel); |
0eda482eb80f
6311933 rdmsr/wrmsr do not need to set/pass values via memory pointers
kucharsk
parents:
0
diff
changeset
|
436 |
wrmsr(PIC_BASE_ADDR + i, cfgs[i]->opt_rawpic); |
0 | 437 |
} |
438 |
||
439 |
for (i = 0; i < 4; i++) { |
|
770
0eda482eb80f
6311933 rdmsr/wrmsr do not need to set/pass values via memory pointers
kucharsk
parents:
0
diff
changeset
|
440 |
wrmsr(PES_BASE_ADDR + i, cfgs[i]->opt_evsel | |
0eda482eb80f
6311933 rdmsr/wrmsr do not need to set/pass values via memory pointers
kucharsk
parents:
0
diff
changeset
|
441 |
(uint64_t)(uintptr_t)OPT_PES_ENABLE); |
0 | 442 |
} |
443 |
} |
|
444 |
||
445 |
static void |
|
446 |
opt_pcbe_allstop(void) |
|
447 |
{ |
|
448 |
int i; |
|
449 |
||
450 |
for (i = 0; i < 4; i++) |
|
770
0eda482eb80f
6311933 rdmsr/wrmsr do not need to set/pass values via memory pointers
kucharsk
parents:
0
diff
changeset
|
451 |
wrmsr(PES_BASE_ADDR + i, 0ULL); |
0 | 452 |
|
453 |
/* |
|
454 |
* Disable non-privileged access to the counter registers. |
|
455 |
*/ |
|
456 |
setcr4((uint32_t)getcr4() & ~CR4_PCE); |
|
457 |
} |
|
458 |
||
459 |
static void |
|
460 |
opt_pcbe_sample(void *token) |
|
461 |
{ |
|
462 |
opt_pcbe_config_t *cfgs[4] = { NULL, NULL, NULL, NULL }; |
|
463 |
opt_pcbe_config_t *pcfg = NULL; |
|
464 |
int i; |
|
465 |
uint64_t curpic[4]; |
|
466 |
uint64_t *addrs[4]; |
|
467 |
uint64_t *tmp; |
|
468 |
int64_t diff; |
|
469 |
||
470 |
for (i = 0; i < 4; i++) |
|
770
0eda482eb80f
6311933 rdmsr/wrmsr do not need to set/pass values via memory pointers
kucharsk
parents:
0
diff
changeset
|
471 |
curpic[i] = rdmsr(PIC_BASE_ADDR); |
0 | 472 |
|
473 |
/* |
|
474 |
* Query kernel for all configs which are co-programmed. |
|
475 |
*/ |
|
476 |
do { |
|
477 |
pcfg = (opt_pcbe_config_t *)kcpc_next_config(token, pcfg, &tmp); |
|
478 |
||
479 |
if (pcfg != NULL) { |
|
480 |
ASSERT(pcfg->opt_picno < 4); |
|
481 |
cfgs[pcfg->opt_picno] = pcfg; |
|
482 |
addrs[pcfg->opt_picno] = tmp; |
|
483 |
} |
|
484 |
} while (pcfg != NULL); |
|
485 |
||
486 |
for (i = 0; i < 4; i++) { |
|
487 |
if (cfgs[i] == NULL) |
|
488 |
continue; |
|
489 |
||
490 |
diff = (curpic[i] - cfgs[i]->opt_rawpic) & MASK48; |
|
491 |
*addrs[i] += diff; |
|
492 |
DTRACE_PROBE4(opt__pcbe__sample, int, i, uint64_t, *addrs[i], |
|
493 |
uint64_t, curpic[i], uint64_t, cfgs[i]->opt_rawpic); |
|
494 |
cfgs[i]->opt_rawpic = *addrs[i] & MASK48; |
|
495 |
} |
|
496 |
} |
|
497 |
||
498 |
static void |
|
499 |
opt_pcbe_free(void *config) |
|
500 |
{ |
|
501 |
kmem_free(config, sizeof (opt_pcbe_config_t)); |
|
502 |
} |
|
503 |
||
504 |
||
505 |
static struct modlpcbe modlpcbe = { |
|
506 |
&mod_pcbeops, |
|
507 |
"AMD Performance Counters v%I%", |
|
508 |
&opt_pcbe_ops |
|
509 |
}; |
|
510 |
||
511 |
static struct modlinkage modl = { |
|
512 |
MODREV_1, |
|
513 |
&modlpcbe, |
|
514 |
}; |
|
515 |
||
516 |
int |
|
517 |
_init(void) |
|
518 |
{ |
|
519 |
int ret; |
|
520 |
||
521 |
if (opt_pcbe_init() != 0) |
|
522 |
return (ENOTSUP); |
|
523 |
||
524 |
if ((ret = mod_install(&modl)) != 0) |
|
525 |
kmem_free(evlist, evlist_sz + 1); |
|
526 |
||
527 |
return (ret); |
|
528 |
} |
|
529 |
||
530 |
int |
|
531 |
_fini(void) |
|
532 |
{ |
|
533 |
int ret; |
|
534 |
||
535 |
if ((ret = mod_remove(&modl)) == 0) |
|
536 |
kmem_free(evlist, evlist_sz + 1); |
|
537 |
return (ret); |
|
538 |
} |
|
539 |
||
540 |
int |
|
541 |
_info(struct modinfo *mi) |
|
542 |
{ |
|
543 |
return (mod_info(&modl, mi)); |
|
544 |
} |