The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/i386/perfmon.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright 1996 Massachusetts Institute of Technology
    3  *
    4  * Permission to use, copy, modify, and distribute this software and
    5  * its documentation for any purpose and without fee is hereby
    6  * granted, provided that both the above copyright notice and this
    7  * permission notice appear in all copies, that both the above
    8  * copyright notice and this permission notice appear in all
    9  * supporting documentation, and that the name of M.I.T. not be used
   10  * in advertising or publicity pertaining to distribution of the
   11  * software without specific, written prior permission.  M.I.T. makes
   12  * no representations about the suitability of this software for any
   13  * purpose.  It is provided "as is" without express or implied
   14  * warranty.
   15  * 
   16  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
   17  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
   18  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
   20  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  * $FreeBSD$
   30  */
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/fcntl.h>
   35 
   36 #ifndef SMP
   37 #include <machine/cputypes.h>
   38 #endif
   39 #include <machine/clock.h>
   40 #include <machine/perfmon.h>
   41 
   42 static int perfmon_inuse;
   43 static int perfmon_cpuok;
   44 #ifndef SMP
   45 static int msr_ctl[NPMC];
   46 #endif
   47 static int msr_pmc[NPMC];
   48 static unsigned int ctl_shadow[NPMC];
   49 static quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */
   50 static int (*writectl)(int);
   51 #ifndef SMP
   52 static int writectl5(int);
   53 static int writectl6(int);
   54 #endif
   55 
   56 /*
   57  * Must be called after cpu_class is set up.
   58  */
   59 void
   60 perfmon_init(void)
   61 {
   62 #ifndef SMP
   63         switch(cpu_class) {
   64         case CPUCLASS_586:
   65                 perfmon_cpuok = 1;
   66                 msr_ctl[0] = 0x11;
   67                 msr_ctl[1] = 0x11;
   68                 msr_pmc[0] = 0x12;
   69                 msr_pmc[1] = 0x13;
   70                 writectl = writectl5;
   71                 break;
   72         case CPUCLASS_686:
   73                 perfmon_cpuok = 1;
   74                 msr_ctl[0] = 0x186;
   75                 msr_ctl[1] = 0x187;
   76                 msr_pmc[0] = 0xc1;
   77                 msr_pmc[1] = 0xc2;
   78                 writectl = writectl6;
   79                 break;
   80 
   81         default:
   82                 perfmon_cpuok = 0;
   83                 break;
   84         }
   85 #endif /* SMP */
   86 }
   87 
   88 int
   89 perfmon_avail(void)
   90 {
   91         return perfmon_cpuok;
   92 }
   93 
   94 int
   95 perfmon_setup(int pmc, unsigned int control)
   96 {
   97         if (pmc < 0 || pmc >= NPMC)
   98                 return EINVAL;
   99 
  100         perfmon_inuse |= (1 << pmc);
  101         control &= ~(PMCF_SYS_FLAGS << 16);
  102         disable_intr();
  103         ctl_shadow[pmc] = control;
  104         writectl(pmc);
  105         wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  106         enable_intr();
  107         return 0;
  108 }
  109 
  110 int
  111 perfmon_get(int pmc, unsigned int *control)
  112 {
  113         if (pmc < 0 || pmc >= NPMC)
  114                 return EINVAL;
  115 
  116         if (perfmon_inuse & (1 << pmc)) {
  117                 *control = ctl_shadow[pmc];
  118                 return 0;
  119         }
  120         return EBUSY;           /* XXX reversed sense */
  121 }
  122 
  123 int
  124 perfmon_fini(int pmc)
  125 {
  126         if (pmc < 0 || pmc >= NPMC)
  127                 return EINVAL;
  128 
  129         if (perfmon_inuse & (1 << pmc)) {
  130                 perfmon_stop(pmc);
  131                 ctl_shadow[pmc] = 0;
  132                 perfmon_inuse &= ~(1 << pmc);
  133                 return 0;
  134         }
  135         return EBUSY;           /* XXX reversed sense */
  136 }
  137 
  138 int
  139 perfmon_start(int pmc)
  140 {
  141         if (pmc < 0 || pmc >= NPMC)
  142                 return EINVAL;
  143 
  144         if (perfmon_inuse & (1 << pmc)) {
  145                 disable_intr();
  146                 ctl_shadow[pmc] |= (PMCF_EN << 16);
  147                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
  148                 writectl(pmc);
  149                 enable_intr();
  150                 return 0;
  151         }
  152         return EBUSY;
  153 }
  154 
  155 int
  156 perfmon_stop(int pmc)
  157 {
  158         if (pmc < 0 || pmc >= NPMC)
  159                 return EINVAL;
  160 
  161         if (perfmon_inuse & (1 << pmc)) {
  162                 disable_intr();
  163                 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
  164                 ctl_shadow[pmc] &= ~(PMCF_EN << 16);
  165                 writectl(pmc);
  166                 enable_intr();
  167                 return 0;
  168         }
  169         return EBUSY;
  170 }
  171 
  172 int
  173 perfmon_read(int pmc, quad_t *val)
  174 {
  175         if (pmc < 0 || pmc >= NPMC)
  176                 return EINVAL;
  177 
  178         if (perfmon_inuse & (1 << pmc)) {
  179                 if (ctl_shadow[pmc] & (PMCF_EN << 16))
  180                         *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
  181                 else
  182                         *val = pmc_shadow[pmc];
  183                 return 0;
  184         }
  185 
  186         return EBUSY;
  187 }
  188 
  189 int
  190 perfmon_reset(int pmc)
  191 {
  192         if (pmc < 0 || pmc >= NPMC)
  193                 return EINVAL;
  194 
  195         if (perfmon_inuse & (1 << pmc)) {
  196                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  197                 return 0;
  198         }
  199         return EBUSY;
  200 }
  201 
  202 #ifndef SMP
  203 /*
  204  * Unfortunately, the performance-monitoring registers are laid out
  205  * differently in the P5 and P6.  We keep everything in P6 format
  206  * internally (except for the event code), and convert to P5
  207  * format as needed on those CPUs.  The writectl function pointer
  208  * is set up to point to one of these functions by perfmon_init().
  209  */
  210 int
  211 writectl6(int pmc)
  212 {
  213         if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
  214                 wrmsr(msr_ctl[pmc], 0);
  215         } else {
  216                 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
  217         }
  218         return 0;
  219 }
  220 
  221 #define P5FLAG_P        0x200
  222 #define P5FLAG_E        0x100
  223 #define P5FLAG_USR      0x80
  224 #define P5FLAG_OS       0x40
  225 
  226 int
  227 writectl5(int pmc)
  228 {
  229         quad_t newval = 0;
  230 
  231         if (ctl_shadow[1] & (PMCF_EN << 16)) {
  232                 if (ctl_shadow[1] & (PMCF_USR << 16))
  233                         newval |= P5FLAG_USR << 16;
  234                 if (ctl_shadow[1] & (PMCF_OS << 16))
  235                         newval |= P5FLAG_OS << 16;
  236                 if (!(ctl_shadow[1] & (PMCF_E << 16)))
  237                         newval |= P5FLAG_E << 16;
  238                 newval |= (ctl_shadow[1] & 0x3f) << 16;
  239         }
  240         if (ctl_shadow[0] & (PMCF_EN << 16)) {
  241                 if (ctl_shadow[0] & (PMCF_USR << 16))
  242                         newval |= P5FLAG_USR;
  243                 if (ctl_shadow[0] & (PMCF_OS << 16))
  244                         newval |= P5FLAG_OS;
  245                 if (!(ctl_shadow[0] & (PMCF_E << 16)))
  246                         newval |= P5FLAG_E;
  247                 newval |= ctl_shadow[0] & 0x3f;
  248         }
  249 
  250         wrmsr(msr_ctl[0], newval);
  251         return 0;               /* XXX should check for unimplemented bits */
  252 }
  253 #endif /* !SMP */
  254 
  255 /*
  256  * Now the user-mode interface, called from a subdevice of mem.c.
  257  */
  258 static int writer;
  259 static int writerpmc;
  260 
  261 int
  262 perfmon_open(dev_t dev, int flags, int fmt, struct proc *p)
  263 {
  264         if (!perfmon_cpuok)
  265                 return ENXIO;
  266 
  267         if (flags & FWRITE) {
  268                 if (writer) {
  269                         return EBUSY;
  270                 } else {
  271                         writer = 1;
  272                         writerpmc = 0;
  273                 }
  274         }
  275         return 0;
  276 }
  277 
  278 int
  279 perfmon_close(dev_t dev, int flags, int fmt, struct proc *p)
  280 {
  281         if (flags & FWRITE) {
  282                 int i;
  283 
  284                 for (i = 0; i < NPMC; i++) {
  285                         if (writerpmc & (1 << i))
  286                                 perfmon_fini(i);
  287                 }
  288                 writer = 0;
  289         }
  290         return 0;
  291 }
  292 
  293 int
  294 perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p)
  295 {
  296         struct pmc *pmc;
  297         struct pmc_data *pmcd;
  298         struct pmc_tstamp *pmct;
  299         int *ip;
  300         int rv;
  301 
  302         switch(cmd) {
  303         case PMIOSETUP:
  304                 if (!(flags & FWRITE))
  305                         return EPERM;
  306                 pmc = (struct pmc *)param;
  307 
  308                 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
  309                 if (!rv) {
  310                         writerpmc |= (1 << pmc->pmc_num);
  311                 }
  312                 break;
  313 
  314         case PMIOGET:
  315                 pmc = (struct pmc *)param;
  316                 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
  317                 break;
  318 
  319         case PMIOSTART:
  320                 if (!(flags & FWRITE))
  321                         return EPERM;
  322 
  323                 ip = (int *)param;
  324                 rv = perfmon_start(*ip);
  325                 break;
  326 
  327         case PMIOSTOP:
  328                 if (!(flags & FWRITE))
  329                         return EPERM;
  330 
  331                 ip = (int *)param;
  332                 rv = perfmon_stop(*ip);
  333                 break;
  334 
  335         case PMIORESET:
  336                 if (!(flags & FWRITE))
  337                         return EPERM;
  338 
  339                 ip = (int *)param;
  340                 rv = perfmon_reset(*ip);
  341                 break;
  342 
  343         case PMIOREAD:
  344                 pmcd = (struct pmc_data *)param;
  345                 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
  346                 break;
  347 
  348         case PMIOTSTAMP:
  349                 if (!tsc_freq) {
  350                         rv = ENOTTY;
  351                         break;
  352                 }
  353                 pmct = (struct pmc_tstamp *)param;
  354                 /* XXX interface loses precision. */
  355                 pmct->pmct_rate = tsc_freq / 1000000;
  356                 pmct->pmct_value = rdtsc();
  357                 rv = 0;
  358                 break;
  359         default:
  360                 rv = ENOTTY;
  361         }
  362 
  363         return rv;
  364 }

Cache object: 249949d5f0eb842ee08efa256b2f7e6f


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.