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.1/sys/i386/i386/perfmon.c 111815 2003-03-03 12:15:54Z phk $
   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         .d_open =       perfmon_open,
   75         .d_close =      perfmon_close,
   76         .d_ioctl =      perfmon_ioctl,
   77         .d_name =       "perfmon",
   78         .d_maj =        CDEV_MAJOR,
   79 };
   80 
   81 /*
   82  * Must be called after cpu_class is set up.
   83  */
   84 void
   85 perfmon_init(void)
   86 {
   87 #ifndef SMP
   88         switch(cpu_class) {
   89         case CPUCLASS_586:
   90                 perfmon_cpuok = 1;
   91                 msr_ctl[0] = 0x11;
   92                 msr_ctl[1] = 0x11;
   93                 msr_pmc[0] = 0x12;
   94                 msr_pmc[1] = 0x13;
   95                 writectl = writectl5;
   96                 break;
   97         case CPUCLASS_686:
   98                 perfmon_cpuok = 1;
   99                 msr_ctl[0] = 0x186;
  100                 msr_ctl[1] = 0x187;
  101                 msr_pmc[0] = 0xc1;
  102                 msr_pmc[1] = 0xc2;
  103                 writectl = writectl6;
  104                 break;
  105 
  106         default:
  107                 perfmon_cpuok = 0;
  108                 break;
  109         }
  110 #endif /* SMP */
  111 }
  112 
  113 static void
  114 perfmon_init_dev(dummy)
  115         void *dummy;
  116 {
  117         make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon");
  118 }
  119 
  120 int
  121 perfmon_avail(void)
  122 {
  123         return perfmon_cpuok;
  124 }
  125 
  126 int
  127 perfmon_setup(int pmc, unsigned int control)
  128 {
  129         register_t      savecrit;
  130 
  131         if (pmc < 0 || pmc >= NPMC)
  132                 return EINVAL;
  133 
  134         perfmon_inuse |= (1 << pmc);
  135         control &= ~(PMCF_SYS_FLAGS << 16);
  136         savecrit = intr_disable();
  137         ctl_shadow[pmc] = control;
  138         writectl(pmc);
  139         wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  140         intr_restore(savecrit);
  141         return 0;
  142 }
  143 
  144 int
  145 perfmon_get(int pmc, unsigned int *control)
  146 {
  147         if (pmc < 0 || pmc >= NPMC)
  148                 return EINVAL;
  149 
  150         if (perfmon_inuse & (1 << pmc)) {
  151                 *control = ctl_shadow[pmc];
  152                 return 0;
  153         }
  154         return EBUSY;           /* XXX reversed sense */
  155 }
  156 
  157 int
  158 perfmon_fini(int pmc)
  159 {
  160         if (pmc < 0 || pmc >= NPMC)
  161                 return EINVAL;
  162 
  163         if (perfmon_inuse & (1 << pmc)) {
  164                 perfmon_stop(pmc);
  165                 ctl_shadow[pmc] = 0;
  166                 perfmon_inuse &= ~(1 << pmc);
  167                 return 0;
  168         }
  169         return EBUSY;           /* XXX reversed sense */
  170 }
  171 
  172 int
  173 perfmon_start(int pmc)
  174 {
  175         register_t      savecrit;
  176 
  177         if (pmc < 0 || pmc >= NPMC)
  178                 return EINVAL;
  179 
  180         if (perfmon_inuse & (1 << pmc)) {
  181                 savecrit = intr_disable();
  182                 ctl_shadow[pmc] |= (PMCF_EN << 16);
  183                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
  184                 writectl(pmc);
  185                 intr_restore(savecrit);
  186                 return 0;
  187         }
  188         return EBUSY;
  189 }
  190 
  191 int
  192 perfmon_stop(int pmc)
  193 {
  194         register_t      savecrit;
  195 
  196         if (pmc < 0 || pmc >= NPMC)
  197                 return EINVAL;
  198 
  199         if (perfmon_inuse & (1 << pmc)) {
  200                 savecrit = intr_disable();
  201                 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
  202                 ctl_shadow[pmc] &= ~(PMCF_EN << 16);
  203                 writectl(pmc);
  204                 intr_restore(savecrit);
  205                 return 0;
  206         }
  207         return EBUSY;
  208 }
  209 
  210 int
  211 perfmon_read(int pmc, quad_t *val)
  212 {
  213         if (pmc < 0 || pmc >= NPMC)
  214                 return EINVAL;
  215 
  216         if (perfmon_inuse & (1 << pmc)) {
  217                 if (ctl_shadow[pmc] & (PMCF_EN << 16))
  218                         *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
  219                 else
  220                         *val = pmc_shadow[pmc];
  221                 return 0;
  222         }
  223 
  224         return EBUSY;
  225 }
  226 
  227 int
  228 perfmon_reset(int pmc)
  229 {
  230         if (pmc < 0 || pmc >= NPMC)
  231                 return EINVAL;
  232 
  233         if (perfmon_inuse & (1 << pmc)) {
  234                 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
  235                 return 0;
  236         }
  237         return EBUSY;
  238 }
  239 
  240 #ifndef SMP
  241 /*
  242  * Unfortunately, the performance-monitoring registers are laid out
  243  * differently in the P5 and P6.  We keep everything in P6 format
  244  * internally (except for the event code), and convert to P5
  245  * format as needed on those CPUs.  The writectl function pointer
  246  * is set up to point to one of these functions by perfmon_init().
  247  */
  248 int
  249 writectl6(int pmc)
  250 {
  251         if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
  252                 wrmsr(msr_ctl[pmc], 0);
  253         } else {
  254                 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
  255         }
  256         return 0;
  257 }
  258 
  259 #define P5FLAG_P        0x200
  260 #define P5FLAG_E        0x100
  261 #define P5FLAG_USR      0x80
  262 #define P5FLAG_OS       0x40
  263 
  264 int
  265 writectl5(int pmc)
  266 {
  267         quad_t newval = 0;
  268 
  269         if (ctl_shadow[1] & (PMCF_EN << 16)) {
  270                 if (ctl_shadow[1] & (PMCF_USR << 16))
  271                         newval |= P5FLAG_USR << 16;
  272                 if (ctl_shadow[1] & (PMCF_OS << 16))
  273                         newval |= P5FLAG_OS << 16;
  274                 if (!(ctl_shadow[1] & (PMCF_E << 16)))
  275                         newval |= P5FLAG_E << 16;
  276                 newval |= (ctl_shadow[1] & 0x3f) << 16;
  277         }
  278         if (ctl_shadow[0] & (PMCF_EN << 16)) {
  279                 if (ctl_shadow[0] & (PMCF_USR << 16))
  280                         newval |= P5FLAG_USR;
  281                 if (ctl_shadow[0] & (PMCF_OS << 16))
  282                         newval |= P5FLAG_OS;
  283                 if (!(ctl_shadow[0] & (PMCF_E << 16)))
  284                         newval |= P5FLAG_E;
  285                 newval |= ctl_shadow[0] & 0x3f;
  286         }
  287 
  288         wrmsr(msr_ctl[0], newval);
  289         return 0;               /* XXX should check for unimplemented bits */
  290 }
  291 #endif /* !SMP */
  292 
  293 /*
  294  * Now the user-mode interface, called from a subdevice of mem.c.
  295  */
  296 static int writer;
  297 static int writerpmc;
  298 
  299 static int
  300 perfmon_open(dev_t dev, int flags, int fmt, struct thread *td)
  301 {
  302         if (!perfmon_cpuok)
  303                 return ENXIO;
  304 
  305         if (flags & FWRITE) {
  306                 if (writer) {
  307                         return EBUSY;
  308                 } else {
  309                         writer = 1;
  310                         writerpmc = 0;
  311                 }
  312         }
  313         return 0;
  314 }
  315 
  316 static int
  317 perfmon_close(dev_t dev, int flags, int fmt, struct thread *td)
  318 {
  319         if (flags & FWRITE) {
  320                 int i;
  321 
  322                 for (i = 0; i < NPMC; i++) {
  323                         if (writerpmc & (1 << i))
  324                                 perfmon_fini(i);
  325                 }
  326                 writer = 0;
  327         }
  328         return 0;
  329 }
  330 
  331 static int
  332 perfmon_ioctl(dev_t dev, u_long cmd, caddr_t param, int flags, struct thread *td)
  333 {
  334         struct pmc *pmc;
  335         struct pmc_data *pmcd;
  336         struct pmc_tstamp *pmct;
  337         int *ip;
  338         int rv;
  339 
  340         switch(cmd) {
  341         case PMIOSETUP:
  342                 if (!(flags & FWRITE))
  343                         return EPERM;
  344                 pmc = (struct pmc *)param;
  345 
  346                 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
  347                 if (!rv) {
  348                         writerpmc |= (1 << pmc->pmc_num);
  349                 }
  350                 break;
  351 
  352         case PMIOGET:
  353                 pmc = (struct pmc *)param;
  354                 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
  355                 break;
  356 
  357         case PMIOSTART:
  358                 if (!(flags & FWRITE))
  359                         return EPERM;
  360 
  361                 ip = (int *)param;
  362                 rv = perfmon_start(*ip);
  363                 break;
  364 
  365         case PMIOSTOP:
  366                 if (!(flags & FWRITE))
  367                         return EPERM;
  368 
  369                 ip = (int *)param;
  370                 rv = perfmon_stop(*ip);
  371                 break;
  372 
  373         case PMIORESET:
  374                 if (!(flags & FWRITE))
  375                         return EPERM;
  376 
  377                 ip = (int *)param;
  378                 rv = perfmon_reset(*ip);
  379                 break;
  380 
  381         case PMIOREAD:
  382                 pmcd = (struct pmc_data *)param;
  383                 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
  384                 break;
  385 
  386         case PMIOTSTAMP:
  387                 if (!tsc_freq) {
  388                         rv = ENOTTY;
  389                         break;
  390                 }
  391                 pmct = (struct pmc_tstamp *)param;
  392                 /* XXX interface loses precision. */
  393                 pmct->pmct_rate = tsc_freq / 1000000;
  394                 pmct->pmct_value = rdtsc();
  395                 rv = 0;
  396                 break;
  397         default:
  398                 rv = ENOTTY;
  399         }
  400 
  401         return rv;
  402 }

Cache object: e1febd68a55bf1f6124973071a96582d


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