hwpmc_tsc.c revision 267654
1302080Shselasky/*- 2302080Shselasky * Copyright (c) 2008 Joseph Koshy 3302080Shselasky * All rights reserved. 4302080Shselasky * 5302080Shselasky * Redistribution and use in source and binary forms, with or without 6302080Shselasky * modification, are permitted provided that the following conditions 7302080Shselasky * are met: 8302080Shselasky * 1. Redistributions of source code must retain the above copyright 9302080Shselasky * notice, this list of conditions and the following disclaimer. 10302080Shselasky * 2. Redistributions in binary form must reproduce the above copyright 11302080Shselasky * notice, this list of conditions and the following disclaimer in the 12302080Shselasky * documentation and/or other materials provided with the distribution. 13302080Shselasky * 14302080Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15302080Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16302080Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17302080Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18302080Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19302080Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20302080Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21302080Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22302080Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23302080Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24302080Shselasky * SUCH DAMAGE. 25302080Shselasky */ 26302080Shselasky 27302080Shselasky#include <sys/cdefs.h> 28302080Shselasky__FBSDID("$FreeBSD: releng/9.3/sys/dev/hwpmc/hwpmc_tsc.c 236238 2012-05-29 14:50:21Z fabient $"); 29302080Shselasky 30302080Shselasky#include <sys/param.h> 31302080Shselasky#include <sys/pmc.h> 32302080Shselasky#include <sys/pmckern.h> 33302080Shselasky#include <sys/systm.h> 34302080Shselasky 35302080Shselasky#include <machine/specialreg.h> 36302080Shselasky 37302080Shselasky/* 38302080Shselasky * TSC support. 39302080Shselasky */ 40302080Shselasky 41302080Shselasky#define TSC_CAPS PMC_CAP_READ 42302080Shselasky 43302080Shselaskystruct tsc_descr { 44302080Shselasky struct pmc_descr pm_descr; /* "base class" */ 45302080Shselasky}; 46302080Shselasky 47302080Shselaskystatic struct tsc_descr tsc_pmcdesc[TSC_NPMCS] = 48302080Shselasky{ 49302080Shselasky { 50302080Shselasky .pm_descr = 51302080Shselasky { 52302080Shselasky .pd_name = "TSC", 53302080Shselasky .pd_class = PMC_CLASS_TSC, 54302080Shselasky .pd_caps = TSC_CAPS, 55302080Shselasky .pd_width = 64 56302080Shselasky } 57302080Shselasky } 58302080Shselasky}; 59302080Shselasky 60302080Shselasky/* 61302080Shselasky * Per-CPU data structure for TSCs. 62302080Shselasky */ 63302080Shselasky 64302080Shselaskystruct tsc_cpu { 65302080Shselasky struct pmc_hw tc_hw; 66302080Shselasky}; 67302080Shselasky 68302080Shselaskystatic struct tsc_cpu **tsc_pcpu; 69302080Shselasky 70302080Shselaskystatic int 71302080Shselaskytsc_allocate_pmc(int cpu, int ri, struct pmc *pm, 72302080Shselasky const struct pmc_op_pmcallocate *a) 73302080Shselasky{ 74302080Shselasky (void) cpu; 75302080Shselasky 76302080Shselasky KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 77302080Shselasky ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 78302080Shselasky KASSERT(ri >= 0 && ri < TSC_NPMCS, 79302080Shselasky ("[tsc,%d] illegal row index %d", __LINE__, ri)); 80302080Shselasky 81302080Shselasky if (a->pm_class != PMC_CLASS_TSC) 82302080Shselasky return (EINVAL); 83302080Shselasky 84302080Shselasky if ((pm->pm_caps & TSC_CAPS) == 0) 85302080Shselasky return (EINVAL); 86302080Shselasky 87302080Shselasky if ((pm->pm_caps & ~TSC_CAPS) != 0) 88302080Shselasky return (EPERM); 89302080Shselasky 90302080Shselasky if (a->pm_ev != PMC_EV_TSC_TSC || 91302080Shselasky a->pm_mode != PMC_MODE_SC) 92302080Shselasky return (EINVAL); 93302080Shselasky 94302080Shselasky return (0); 95302080Shselasky} 96302080Shselasky 97302080Shselaskystatic int 98302080Shselaskytsc_config_pmc(int cpu, int ri, struct pmc *pm) 99302080Shselasky{ 100302080Shselasky struct pmc_hw *phw; 101302080Shselasky 102302080Shselasky PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm); 103302080Shselasky 104302080Shselasky KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 105302080Shselasky ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 106302080Shselasky KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 107302080Shselasky 108302080Shselasky phw = &tsc_pcpu[cpu]->tc_hw; 109302080Shselasky 110302080Shselasky KASSERT(pm == NULL || phw->phw_pmc == NULL, 111302080Shselasky ("[tsc,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__, 112302080Shselasky pm, phw->phw_pmc)); 113302080Shselasky 114302080Shselasky phw->phw_pmc = pm; 115302080Shselasky 116302080Shselasky return (0); 117302080Shselasky} 118302080Shselasky 119302080Shselaskystatic int 120302080Shselaskytsc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc) 121302080Shselasky{ 122302080Shselasky int error; 123302080Shselasky size_t copied; 124302080Shselasky const struct tsc_descr *pd; 125302080Shselasky struct pmc_hw *phw; 126302080Shselasky 127302080Shselasky KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 128302080Shselasky ("[tsc,%d] illegal CPU %d", __LINE__, cpu)); 129302080Shselasky KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 130302080Shselasky 131302080Shselasky phw = &tsc_pcpu[cpu]->tc_hw; 132302080Shselasky pd = &tsc_pmcdesc[ri]; 133302080Shselasky 134302080Shselasky if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name, 135302080Shselasky PMC_NAME_MAX, &copied)) != 0) 136302080Shselasky return (error); 137302080Shselasky 138302080Shselasky pi->pm_class = pd->pm_descr.pd_class; 139302080Shselasky 140302080Shselasky if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 141302080Shselasky pi->pm_enabled = TRUE; 142302080Shselasky *ppmc = phw->phw_pmc; 143302080Shselasky } else { 144302080Shselasky pi->pm_enabled = FALSE; 145302080Shselasky *ppmc = NULL; 146302080Shselasky } 147302080Shselasky 148302080Shselasky return (0); 149302080Shselasky} 150302080Shselasky 151302080Shselaskystatic int 152302080Shselaskytsc_get_config(int cpu, int ri, struct pmc **ppm) 153302080Shselasky{ 154302080Shselasky (void) ri; 155302080Shselasky 156302080Shselasky KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 157302080Shselasky ("[tsc,%d] illegal CPU %d", __LINE__, cpu)); 158302080Shselasky KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 159302080Shselasky 160302080Shselasky *ppm = tsc_pcpu[cpu]->tc_hw.phw_pmc; 161302080Shselasky 162302080Shselasky return (0); 163302080Shselasky} 164302080Shselasky 165302080Shselaskystatic int 166302080Shselaskytsc_get_msr(int ri, uint32_t *msr) 167302080Shselasky{ 168302080Shselasky (void) ri; 169302080Shselasky 170302080Shselasky KASSERT(ri >= 0 && ri < TSC_NPMCS, 171302080Shselasky ("[tsc,%d] ri %d out of range", __LINE__, ri)); 172302080Shselasky 173302080Shselasky *msr = MSR_TSC; 174302080Shselasky 175302080Shselasky return (0); 176302080Shselasky} 177302080Shselasky 178302080Shselaskystatic int 179302080Shselaskytsc_pcpu_fini(struct pmc_mdep *md, int cpu) 180302080Shselasky{ 181302080Shselasky int ri; 182302080Shselasky struct pmc_cpu *pc; 183302080Shselasky 184302080Shselasky KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 185302080Shselasky ("[tsc,%d] illegal cpu %d", __LINE__, cpu)); 186302080Shselasky KASSERT(tsc_pcpu[cpu] != NULL, ("[tsc,%d] null pcpu", __LINE__)); 187302080Shselasky 188302080Shselasky free(tsc_pcpu[cpu], M_PMC); 189302080Shselasky tsc_pcpu[cpu] = NULL; 190302080Shselasky 191302080Shselasky ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri; 192302080Shselasky 193302080Shselasky pc = pmc_pcpu[cpu]; 194302080Shselasky pc->pc_hwpmcs[ri] = NULL; 195302080Shselasky 196302080Shselasky return (0); 197302080Shselasky} 198302080Shselasky 199302080Shselaskystatic int 200302080Shselaskytsc_pcpu_init(struct pmc_mdep *md, int cpu) 201302080Shselasky{ 202302080Shselasky int ri; 203302080Shselasky struct pmc_cpu *pc; 204302080Shselasky struct tsc_cpu *tsc_pc; 205302080Shselasky 206302080Shselasky 207302080Shselasky KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 208302080Shselasky ("[tsc,%d] illegal cpu %d", __LINE__, cpu)); 209302080Shselasky KASSERT(tsc_pcpu, ("[tsc,%d] null pcpu", __LINE__)); 210302080Shselasky KASSERT(tsc_pcpu[cpu] == NULL, ("[tsc,%d] non-null per-cpu", 211302080Shselasky __LINE__)); 212302080Shselasky 213302080Shselasky tsc_pc = malloc(sizeof(struct tsc_cpu), M_PMC, M_WAITOK|M_ZERO); 214302080Shselasky 215302080Shselasky tsc_pc->tc_hw.phw_state = PMC_PHW_FLAG_IS_ENABLED | 216302080Shselasky PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(0) | 217302080Shselasky PMC_PHW_FLAG_IS_SHAREABLE; 218302080Shselasky 219302080Shselasky tsc_pcpu[cpu] = tsc_pc; 220302080Shselasky 221302080Shselasky ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri; 222302080Shselasky 223302080Shselasky KASSERT(pmc_pcpu, ("[tsc,%d] null generic pcpu", __LINE__)); 224302080Shselasky 225302080Shselasky pc = pmc_pcpu[cpu]; 226302080Shselasky 227302080Shselasky KASSERT(pc, ("[tsc,%d] null generic per-cpu", __LINE__)); 228302080Shselasky 229302080Shselasky pc->pc_hwpmcs[ri] = &tsc_pc->tc_hw; 230302080Shselasky 231302080Shselasky return (0); 232302080Shselasky} 233302080Shselasky 234302080Shselaskystatic int 235302080Shselaskytsc_read_pmc(int cpu, int ri, pmc_value_t *v) 236302080Shselasky{ 237302080Shselasky struct pmc *pm; 238 enum pmc_mode mode; 239 const struct pmc_hw *phw; 240 241 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 242 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 243 KASSERT(ri == 0, ("[tsc,%d] illegal ri %d", __LINE__, ri)); 244 245 phw = &tsc_pcpu[cpu]->tc_hw; 246 pm = phw->phw_pmc; 247 248 KASSERT(pm != NULL, 249 ("[tsc,%d] no owner for PHW [cpu%d,pmc%d]", __LINE__, cpu, ri)); 250 251 mode = PMC_TO_MODE(pm); 252 253 KASSERT(mode == PMC_MODE_SC, 254 ("[tsc,%d] illegal pmc mode %d", __LINE__, mode)); 255 256 PMCDBG(MDP,REA,1,"tsc-read id=%d", ri); 257 258 *v = rdtsc(); 259 260 return (0); 261} 262 263static int 264tsc_release_pmc(int cpu, int ri, struct pmc *pmc) 265{ 266 struct pmc_hw *phw; 267 268 (void) pmc; 269 270 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 271 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 272 KASSERT(ri == 0, 273 ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 274 275 phw = &tsc_pcpu[cpu]->tc_hw; 276 277 KASSERT(phw->phw_pmc == NULL, 278 ("[tsc,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc)); 279 280 /* 281 * Nothing to do. 282 */ 283 return (0); 284} 285 286static int 287tsc_start_pmc(int cpu, int ri) 288{ 289 (void) cpu; 290 291 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 292 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 293 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 294 295 return (0); /* TSCs are always running. */ 296} 297 298static int 299tsc_stop_pmc(int cpu, int ri) 300{ 301 (void) cpu; (void) ri; 302 303 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 304 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 305 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 306 307 return (0); /* Cannot actually stop a TSC. */ 308} 309 310static int 311tsc_write_pmc(int cpu, int ri, pmc_value_t v) 312{ 313 (void) cpu; (void) ri; (void) v; 314 315 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 316 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu)); 317 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri)); 318 319 /* 320 * The TSCs are used as timecounters by the kernel, so even 321 * though some i386 CPUs support writeable TSCs, we don't 322 * support writing changing TSC values through the HWPMC API. 323 */ 324 return (0); 325} 326 327int 328pmc_tsc_initialize(struct pmc_mdep *md, int maxcpu) 329{ 330 struct pmc_classdep *pcd; 331 332 KASSERT(md != NULL, ("[tsc,%d] md is NULL", __LINE__)); 333 KASSERT(md->pmd_nclass >= 1, ("[tsc,%d] dubious md->nclass %d", 334 __LINE__, md->pmd_nclass)); 335 336 tsc_pcpu = malloc(sizeof(struct tsc_cpu *) * maxcpu, M_PMC, 337 M_ZERO|M_WAITOK); 338 339 pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC]; 340 341 pcd->pcd_caps = PMC_CAP_READ; 342 pcd->pcd_class = PMC_CLASS_TSC; 343 pcd->pcd_num = TSC_NPMCS; 344 pcd->pcd_ri = md->pmd_npmc; 345 pcd->pcd_width = 64; 346 347 pcd->pcd_allocate_pmc = tsc_allocate_pmc; 348 pcd->pcd_config_pmc = tsc_config_pmc; 349 pcd->pcd_describe = tsc_describe; 350 pcd->pcd_get_config = tsc_get_config; 351 pcd->pcd_get_msr = tsc_get_msr; 352 pcd->pcd_pcpu_init = tsc_pcpu_init; 353 pcd->pcd_pcpu_fini = tsc_pcpu_fini; 354 pcd->pcd_read_pmc = tsc_read_pmc; 355 pcd->pcd_release_pmc = tsc_release_pmc; 356 pcd->pcd_start_pmc = tsc_start_pmc; 357 pcd->pcd_stop_pmc = tsc_stop_pmc; 358 pcd->pcd_write_pmc = tsc_write_pmc; 359 360 md->pmd_npmc += TSC_NPMCS; 361 362 return (0); 363} 364 365void 366pmc_tsc_finalize(struct pmc_mdep *md) 367{ 368#ifdef INVARIANTS 369 int i, ncpus; 370 371 ncpus = pmc_cpu_max(); 372 for (i = 0; i < ncpus; i++) 373 KASSERT(tsc_pcpu[i] == NULL, ("[tsc,%d] non-null pcpu cpu %d", 374 __LINE__, i)); 375 376 KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_class == 377 PMC_CLASS_TSC, ("[tsc,%d] class mismatch", __LINE__)); 378 379#else 380 (void) md; 381#endif 382 383 free(tsc_pcpu, M_PMC); 384 tsc_pcpu = NULL; 385} 386