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: src/sys/i386/i386/perfmon.c,v 1.8.2.1 1999/09/05 08:11:13 peter Exp $
   30  */
   31 
   32 #include "opt_cpu.h"
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/fcntl.h>
   37 #include <sys/ioccom.h>
   38 
   39 #include <machine/cpu.h>
   40 #include <machine/cputypes.h>
   41 #include <machine/clock.h>
   42 #include <machine/perfmon.h>
   43 
   44 static int perfmon_inuse;
   45 static int perfmon_cpuok;
   46 static int msr_ctl[NPMC];
   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 static int writectl5(int);
   52 static int writectl6(int);
   53 
   54 /*
   55  * Must be called after cpu_class is set up.
   56  */
   57 void
   58 perfmon_init(void)
   59 {
   60         switch(cpu_class) {
   61         case CPUCLASS_586:
   62                 perfmon_cpuok = 1;
   63                 msr_ctl[0] = 0x11;
   64                 msr_ctl[1] = 0x11;
   65                 msr_pmc[0] = 0x12;
   66                 msr_pmc[1] = 0x13;
   67                 writectl = writectl5;
   68                 break;
   69         case CPUCLASS_686:
   70                 perfmon_cpuok = 1;
   71                 msr_ctl[0] = 0x186;
   72                 msr_ctl[1] = 0x187;
   73                 msr_pmc[0] = 0xc1;
   74                 msr_pmc[1] = 0xc2;
   75                 writectl = writectl6;
   76                 break;
   77 
   78         default:
   79                 perfmon_cpuok = 0;
   80                 break;
   81         }
   82 }
   83 
   84 int
   85 perfmon_avail(void)
   86 {
   87         return perfmon_cpuok;
   88 }
   89 
   90 int
   91 perfmon_setup(int pmc, unsigned int control)
   92 {
   93         if (pmc < 0 || pmc >= NPMC)
   94                 return EINVAL;
   95 
   96         perfmon_inuse |= (1 << pmc);
   97         control &= ~(PMCF_SYS_FLAGS << 16);
   98         disable_intr();
   99         ctl_shadow[pmc] = control;
  100         writectl(pmc);
  101         wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  102         enable_intr();
  103         return 0;
  104 }
  105 
  106 int
  107 perfmon_get(int pmc, unsigned int *control)
  108 {
  109         if (pmc < 0 || pmc >= NPMC)
  110                 return EINVAL;
  111 
  112         if (perfmon_inuse & (1 << pmc)) {
  113                 *control = ctl_shadow[pmc];
  114                 return 0;
  115         }
  116         return EBUSY;           /* XXX reversed sense */
  117 }
  118 
  119 int
  120 perfmon_fini(int pmc)
  121 {
  122         if (pmc < 0 || pmc >= NPMC)
  123                 return EINVAL;
  124 
  125         if (perfmon_inuse & (1 << pmc)) {
  126                 perfmon_stop(pmc);
  127                 ctl_shadow[pmc] = 0;
  128                 perfmon_inuse &= ~(1 << pmc);
  129                 return 0;
  130         }
  131         return EBUSY;           /* XXX reversed sense */
  132 }
  133 
  134 int
  135 perfmon_start(int pmc)
  136 {
  137         if (pmc < 0 || pmc >= NPMC)
  138                 return EINVAL;
  139 
  140         if (perfmon_inuse & (1 << pmc)) {
  141                 disable_intr();
  142                 ctl_shadow[pmc] |= (PMCF_EN << 16);
  143                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
  144                 writectl(pmc);
  145                 enable_intr();
  146                 return 0;
  147         }
  148         return EBUSY;
  149 }
  150 
  151 int
  152 perfmon_stop(int pmc)
  153 {
  154         if (pmc < 0 || pmc >= NPMC)
  155                 return EINVAL;
  156 
  157         if (perfmon_inuse & (1 << pmc)) {
  158                 disable_intr();
  159                 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]);
  160                 ctl_shadow[pmc] &= ~(PMCF_EN << 16);
  161                 writectl(pmc);
  162                 enable_intr();
  163                 return 0;
  164         }
  165         return EBUSY;
  166 }
  167 
  168 int
  169 perfmon_read(int pmc, quad_t *val)
  170 {
  171         if (pmc < 0 || pmc >= NPMC)
  172                 return EINVAL;
  173 
  174         if (perfmon_inuse & (1 << pmc)) {
  175                 if (ctl_shadow[pmc] & (PMCF_EN << 16))
  176                         *val = rdmsr(msr_pmc[pmc]);
  177                 else
  178                         *val = pmc_shadow[pmc];
  179                 return 0;
  180         }
  181 
  182         return EBUSY;
  183 }
  184 
  185 int
  186 perfmon_reset(int pmc)
  187 {
  188         if (pmc < 0 || pmc >= NPMC)
  189                 return EINVAL;
  190 
  191         if (perfmon_inuse & (1 << pmc)) {
  192                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  193                 return 0;
  194         }
  195         return EBUSY;
  196 }
  197 
  198 /*
  199  * Unfortunately, the performance-monitoring registers are laid out
  200  * differently in the P5 and P6.  We keep everything in P6 format
  201  * internally (except for the event code), and convert to P5
  202  * format as needed on those CPUs.  The writectl function pointer
  203  * is set up to point to one of these functions by perfmon_init().
  204  */
  205 int
  206 writectl6(int pmc)
  207 {
  208         if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
  209                 wrmsr(msr_ctl[pmc], 0);
  210         } else {
  211                 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
  212         }
  213         return 0;
  214 }
  215 
  216 #define P5FLAG_P        0x200
  217 #define P5FLAG_E        0x100
  218 #define P5FLAG_USR      0x80
  219 #define P5FLAG_OS       0x40
  220 
  221 int
  222 writectl5(int pmc)
  223 {
  224         quad_t newval = 0;
  225 
  226         if (ctl_shadow[1] & (PMCF_EN << 16)) {
  227                 if (ctl_shadow[1] & (PMCF_USR << 16))
  228                         newval |= P5FLAG_USR << 16;
  229                 if (ctl_shadow[1] & (PMCF_OS << 16))
  230                         newval |= P5FLAG_OS << 16;
  231                 if (!(ctl_shadow[1] & (PMCF_E << 16)))
  232                         newval |= P5FLAG_E << 16;
  233                 newval |= (ctl_shadow[1] & 0x3f) << 16;
  234         }
  235         if (ctl_shadow[0] & (PMCF_EN << 16)) {
  236                 if (ctl_shadow[0] & (PMCF_USR << 16))
  237                         newval |= P5FLAG_USR;
  238                 if (ctl_shadow[0] & (PMCF_OS << 16))
  239                         newval |= P5FLAG_OS;
  240                 if (!(ctl_shadow[0] & (PMCF_E << 16)))
  241                         newval |= P5FLAG_E;
  242                 newval |= ctl_shadow[0] & 0x3f;
  243         }
  244 
  245         wrmsr(msr_ctl[0], newval);
  246         return 0;               /* XXX should check for unimplemented bits */
  247 }
  248 
  249 /*
  250  * Now the user-mode interface, called from a subdevice of mem.c.
  251  */
  252 static int writer;
  253 static int writerpmc;
  254 
  255 int
  256 perfmon_open(dev_t dev, int flags, int fmt, struct proc *p)
  257 {
  258         if (!perfmon_cpuok)
  259                 return ENXIO;
  260 
  261         if (flags & FWRITE) {
  262                 if (writer) {
  263                         return EBUSY;
  264                 } else {
  265                         writer = 1;
  266                         writerpmc = 0;
  267                 }
  268         }
  269         return 0;
  270 }
  271 
  272 int
  273 perfmon_close(dev_t dev, int flags, int fmt, struct proc *p)
  274 {
  275         if (flags & FWRITE) {
  276                 int i;
  277 
  278                 for (i = 0; i < NPMC; i++) {
  279                         if (writerpmc & (1 << i))
  280                                 perfmon_fini(i);
  281                 }
  282                 writer = 0;
  283         }
  284         return 0;
  285 }
  286 
  287 int
  288 perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p)
  289 {
  290         struct pmc *pmc;
  291         struct pmc_data *pmcd;
  292         struct pmc_tstamp *pmct;
  293         int *ip;
  294         int rv;
  295 
  296         switch(cmd) {
  297         case PMIOSETUP:
  298                 if (!(flags & FWRITE))
  299                         return EPERM;
  300                 pmc = (struct pmc *)param;
  301 
  302                 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
  303                 if (!rv) {
  304                         writerpmc |= (1 << pmc->pmc_num);
  305                 }
  306                 break;
  307 
  308         case PMIOGET:
  309                 pmc = (struct pmc *)param;
  310                 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
  311                 break;
  312 
  313         case PMIOSTART:
  314                 if (!(flags & FWRITE))
  315                         return EPERM;
  316 
  317                 ip = (int *)param;
  318                 rv = perfmon_start(*ip);
  319                 break;
  320 
  321         case PMIOSTOP:
  322                 if (!(flags & FWRITE))
  323                         return EPERM;
  324 
  325                 ip = (int *)param;
  326                 rv = perfmon_stop(*ip);
  327                 break;
  328 
  329         case PMIORESET:
  330                 if (!(flags & FWRITE))
  331                         return EPERM;
  332 
  333                 ip = (int *)param;
  334                 rv = perfmon_reset(*ip);
  335                 break;
  336 
  337         case PMIOREAD:
  338                 pmcd = (struct pmc_data *)param;
  339                 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
  340                 break;
  341 
  342         case PMIOTSTAMP:
  343                 pmct = (struct pmc_tstamp *)param;
  344                 /* XXX interface loses precision. */
  345                 pmct->pmct_rate = i586_ctr_freq / 1000000;
  346                 pmct->pmct_value = rdtsc();
  347                 rv = 0;
  348                 break;
  349 
  350         default:
  351                 rv = ENOTTY;
  352         }
  353 
  354         return rv;
  355 }

Cache object: ce0502e2dad728af259c769d7db91043


[ 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.