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: releng/5.0/sys/i386/i386/perfmon.c 92860 2002-03-21 06:19:08Z imp $
   30  */
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/conf.h>
   35 #include <sys/fcntl.h>
   36 #include <sys/kernel.h>
   37 
   38 #ifndef SMP
   39 #include <machine/cputypes.h>
   40 #endif
   41 #include <machine/clock.h>
   42 #include <machine/perfmon.h>
   43 
   44 static int perfmon_inuse;
   45 static int perfmon_cpuok;
   46 #ifndef SMP
   47 static int msr_ctl[NPMC];
   48 #endif
   49 static int msr_pmc[NPMC];
   50 static unsigned int ctl_shadow[NPMC];
   51 static quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */
   52 static int (*writectl)(int);
   53 #ifndef SMP
   54 static int writectl5(int);
   55 static int writectl6(int);
   56 #endif
   57 
   58 static d_close_t perfmon_close;
   59 static d_open_t perfmon_open;
   60 static d_ioctl_t perfmon_ioctl;
   61 
   62 /*
   63  * XXX perfmon_init_dev(void *) is a split from the perfmon_init() funtion. 
   64  * This solves a problem for DEVFS users.  It loads the "perfmon" driver after
   65  * the DEVFS subsystem has been kicked into action.  The SI_ORDER_ANY is to
   66  * assure that it is the most lowest priority task which, guarantees the
   67  * above.
   68  */
   69 static void perfmon_init_dev(void *);
   70 SYSINIT(cpu, SI_SUB_DRIVERS, SI_ORDER_ANY, perfmon_init_dev, NULL);
   71 
   72 #define CDEV_MAJOR 2    /* We're really a minor of mem.c */
   73 static struct cdevsw perfmon_cdevsw = {
   74         /* open */      perfmon_open,
   75         /* close */     perfmon_close,
   76         /* read */      noread,
   77         /* write */     nowrite,
   78         /* ioctl */     perfmon_ioctl,
   79         /* poll */      nopoll,
   80         /* mmap */      nommap,
   81         /* strategy */  nostrategy,
   82         /* name */      "perfmon",
   83         /* maj */       CDEV_MAJOR,
   84         /* dump */      nodump,
   85         /* psize */     nopsize,
   86         /* flags */     0,
   87 };
   88 
   89 /*
   90  * Must be called after cpu_class is set up.
   91  */
   92 void
   93 perfmon_init(void)
   94 {
   95 #ifndef SMP
   96         switch(cpu_class) {
   97         case CPUCLASS_586:
   98                 perfmon_cpuok = 1;
   99                 msr_ctl[0] = 0x11;
  100                 msr_ctl[1] = 0x11;
  101                 msr_pmc[0] = 0x12;
  102                 msr_pmc[1] = 0x13;
  103                 writectl = writectl5;
  104                 break;
  105         case CPUCLASS_686:
  106                 perfmon_cpuok = 1;
  107                 msr_ctl[0] = 0x186;
  108                 msr_ctl[1] = 0x187;
  109                 msr_pmc[0] = 0xc1;
  110                 msr_pmc[1] = 0xc2;
  111                 writectl = writectl6;
  112                 break;
  113 
  114         default:
  115                 perfmon_cpuok = 0;
  116                 break;
  117         }
  118 #endif /* SMP */
  119 }
  120 
  121 static void
  122 perfmon_init_dev(dummy)
  123         void *dummy;
  124 {
  125         make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon");
  126 }
  127 
  128 int
  129 perfmon_avail(void)
  130 {
  131         return perfmon_cpuok;
  132 }
  133 
  134 int
  135 perfmon_setup(int pmc, unsigned int control)
  136 {
  137         register_t      savecrit;
  138 
  139         if (pmc < 0 || pmc >= NPMC)
  140                 return EINVAL;
  141 
  142         perfmon_inuse |= (1 << pmc);
  143         control &= ~(PMCF_SYS_FLAGS << 16);
  144         savecrit = intr_disable();
  145         ctl_shadow[pmc] = control;
  146         writectl(pmc);
  147         wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  148         intr_restore(savecrit);
  149         return 0;
  150 }
  151 
  152 int
  153 perfmon_get(int pmc, unsigned int *control)
  154 {
  155         if (pmc < 0 || pmc >= NPMC)
  156                 return EINVAL;
  157 
  158         if (perfmon_inuse & (1 << pmc)) {
  159                 *control = ctl_shadow[pmc];
  160                 return 0;
  161         }
  162         return EBUSY;           /* XXX reversed sense */
  163 }
  164 
  165 int
  166 perfmon_fini(int pmc)
  167 {
  168         if (pmc < 0 || pmc >= NPMC)
  169                 return EINVAL;
  170 
  171         if (perfmon_inuse & (1 << pmc)) {
  172                 perfmon_stop(pmc);
  173                 ctl_shadow[pmc] = 0;
  174                 perfmon_inuse &= ~(1 << pmc);
  175                 return 0;
  176         }
  177         return EBUSY;           /* XXX reversed sense */
  178 }
  179 
  180 int
  181 perfmon_start(int pmc)
  182 {
  183         register_t      savecrit;
  184 
  185         if (pmc < 0 || pmc >= NPMC)
  186                 return EINVAL;
  187 
  188         if (perfmon_inuse & (1 << pmc)) {
  189                 savecrit = intr_disable();
  190                 ctl_shadow[pmc] |= (PMCF_EN << 16);
  191                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
  192                 writectl(pmc);
  193                 intr_restore(savecrit);
  194                 return 0;
  195         }
  196         return EBUSY;
  197 }
  198 
  199 int
  200 perfmon_stop(int pmc)
  201 {
  202         register_t      savecrit;
  203 
  204         if (pmc < 0 || pmc >= NPMC)
  205                 return EINVAL;
  206 
  207         if (perfmon_inuse & (1 << pmc)) {
  208                 savecrit = intr_disable();
  209                 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
  210                 ctl_shadow[pmc] &= ~(PMCF_EN << 16);
  211                 writectl(pmc);
  212                 intr_restore(savecrit);
  213                 return 0;
  214         }
  215         return EBUSY;
  216 }
  217 
  218 int
  219 perfmon_read(int pmc, quad_t *val)
  220 {
  221         if (pmc < 0 || pmc >= NPMC)
  222                 return EINVAL;
  223 
  224         if (perfmon_inuse & (1 << pmc)) {
  225                 if (ctl_shadow[pmc] & (PMCF_EN << 16))
  226                         *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
  227                 else
  228                         *val = pmc_shadow[pmc];
  229                 return 0;
  230         }
  231 
  232         return EBUSY;
  233 }
  234 
  235 int
  236 perfmon_reset(int pmc)
  237 {
  238         if (pmc < 0 || pmc >= NPMC)
  239                 return EINVAL;
  240 
  241         if (perfmon_inuse & (1 << pmc)) {
  242                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  243                 return 0;
  244         }
  245         return EBUSY;
  246 }
  247 
  248 #ifndef SMP
  249 /*
  250  * Unfortunately, the performance-monitoring registers are laid out
  251  * differently in the P5 and P6.  We keep everything in P6 format
  252  * internally (except for the event code), and convert to P5
  253  * format as needed on those CPUs.  The writectl function pointer
  254  * is set up to point to one of these functions by perfmon_init().
  255  */
  256 int
  257 writectl6(int pmc)
  258 {
  259         if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
  260                 wrmsr(msr_ctl[pmc], 0);
  261         } else {
  262                 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
  263         }
  264         return 0;
  265 }
  266 
  267 #define P5FLAG_P        0x200
  268 #define P5FLAG_E        0x100
  269 #define P5FLAG_USR      0x80
  270 #define P5FLAG_OS       0x40
  271 
  272 int
  273 writectl5(int pmc)
  274 {
  275         quad_t newval = 0;
  276 
  277         if (ctl_shadow[1] & (PMCF_EN << 16)) {
  278                 if (ctl_shadow[1] & (PMCF_USR << 16))
  279                         newval |= P5FLAG_USR << 16;
  280                 if (ctl_shadow[1] & (PMCF_OS << 16))
  281                         newval |= P5FLAG_OS << 16;
  282                 if (!(ctl_shadow[1] & (PMCF_E << 16)))
  283                         newval |= P5FLAG_E << 16;
  284                 newval |= (ctl_shadow[1] & 0x3f) << 16;
  285         }
  286         if (ctl_shadow[0] & (PMCF_EN << 16)) {
  287                 if (ctl_shadow[0] & (PMCF_USR << 16))
  288                         newval |= P5FLAG_USR;
  289                 if (ctl_shadow[0] & (PMCF_OS << 16))
  290                         newval |= P5FLAG_OS;
  291                 if (!(ctl_shadow[0] & (PMCF_E << 16)))
  292                         newval |= P5FLAG_E;
  293                 newval |= ctl_shadow[0] & 0x3f;
  294         }
  295 
  296         wrmsr(msr_ctl[0], newval);
  297         return 0;               /* XXX should check for unimplemented bits */
  298 }
  299 #endif /* !SMP */
  300 
  301 /*
  302  * Now the user-mode interface, called from a subdevice of mem.c.
  303  */
  304 static int writer;
  305 static int writerpmc;
  306 
  307 static int
  308 perfmon_open(dev_t dev, int flags, int fmt, struct thread *td)
  309 {
  310         if (!perfmon_cpuok)
  311                 return ENXIO;
  312 
  313         if (flags & FWRITE) {
  314                 if (writer) {
  315                         return EBUSY;
  316                 } else {
  317                         writer = 1;
  318                         writerpmc = 0;
  319                 }
  320         }
  321         return 0;
  322 }
  323 
  324 static int
  325 perfmon_close(dev_t dev, int flags, int fmt, struct thread *td)
  326 {
  327         if (flags & FWRITE) {
  328                 int i;
  329 
  330                 for (i = 0; i < NPMC; i++) {
  331                         if (writerpmc & (1 << i))
  332                                 perfmon_fini(i);
  333                 }
  334                 writer = 0;
  335         }
  336         return 0;
  337 }
  338 
  339 static int
  340 perfmon_ioctl(dev_t dev, u_long cmd, caddr_t param, int flags, struct thread *td)
  341 {
  342         struct pmc *pmc;
  343         struct pmc_data *pmcd;
  344         struct pmc_tstamp *pmct;
  345         int *ip;
  346         int rv;
  347 
  348         switch(cmd) {
  349         case PMIOSETUP:
  350                 if (!(flags & FWRITE))
  351                         return EPERM;
  352                 pmc = (struct pmc *)param;
  353 
  354                 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
  355                 if (!rv) {
  356                         writerpmc |= (1 << pmc->pmc_num);
  357                 }
  358                 break;
  359 
  360         case PMIOGET:
  361                 pmc = (struct pmc *)param;
  362                 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
  363                 break;
  364 
  365         case PMIOSTART:
  366                 if (!(flags & FWRITE))
  367                         return EPERM;
  368 
  369                 ip = (int *)param;
  370                 rv = perfmon_start(*ip);
  371                 break;
  372 
  373         case PMIOSTOP:
  374                 if (!(flags & FWRITE))
  375                         return EPERM;
  376 
  377                 ip = (int *)param;
  378                 rv = perfmon_stop(*ip);
  379                 break;
  380 
  381         case PMIORESET:
  382                 if (!(flags & FWRITE))
  383                         return EPERM;
  384 
  385                 ip = (int *)param;
  386                 rv = perfmon_reset(*ip);
  387                 break;
  388 
  389         case PMIOREAD:
  390                 pmcd = (struct pmc_data *)param;
  391                 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
  392                 break;
  393 
  394         case PMIOTSTAMP:
  395                 if (!tsc_freq) {
  396                         rv = ENOTTY;
  397                         break;
  398                 }
  399                 pmct = (struct pmc_tstamp *)param;
  400                 /* XXX interface loses precision. */
  401                 pmct->pmct_rate = tsc_freq / 1000000;
  402                 pmct->pmct_value = rdtsc();
  403                 rv = 0;
  404                 break;
  405         default:
  406                 rv = ENOTTY;
  407         }
  408 
  409         return rv;
  410 }

Cache object: daeb1c9dacd5359855610421b15dcfab


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