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/dev/hwpmc/hwpmc_logging.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 (c) 2005-2007 Joseph Koshy
    3  * Copyright (c) 2007 The FreeBSD Foundation
    4  * All rights reserved.
    5  *
    6  * Portions of this software were developed by A. Joseph Koshy under
    7  * sponsorship from the FreeBSD Foundation and Google, Inc.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  *
   30  */
   31 
   32 /*
   33  * Logging code for hwpmc(4)
   34  */
   35 
   36 #include <sys/cdefs.h>
   37 __FBSDID("$FreeBSD: releng/7.3/sys/dev/hwpmc/hwpmc_logging.c 200011 2009-12-01 23:07:38Z fabient $");
   38 
   39 #include <sys/param.h>
   40 #include <sys/file.h>
   41 #include <sys/kernel.h>
   42 #include <sys/kthread.h>
   43 #include <sys/lock.h>
   44 #include <sys/module.h>
   45 #include <sys/mutex.h>
   46 #include <sys/pmc.h>
   47 #include <sys/pmckern.h>
   48 #include <sys/pmclog.h>
   49 #include <sys/proc.h>
   50 #include <sys/signalvar.h>
   51 #include <sys/sysctl.h>
   52 #include <sys/systm.h>
   53 #include <sys/uio.h>
   54 #include <sys/unistd.h>
   55 #include <sys/vnode.h>
   56 
   57 /*
   58  * Sysctl tunables
   59  */
   60 
   61 SYSCTL_DECL(_kern_hwpmc);
   62 
   63 /*
   64  * kern.hwpmc.logbuffersize -- size of the per-cpu owner buffers.
   65  */
   66 
   67 static int pmclog_buffer_size = PMC_LOG_BUFFER_SIZE;
   68 TUNABLE_INT(PMC_SYSCTL_NAME_PREFIX "logbuffersize", &pmclog_buffer_size);
   69 SYSCTL_INT(_kern_hwpmc, OID_AUTO, logbuffersize, CTLFLAG_TUN|CTLFLAG_RD,
   70     &pmclog_buffer_size, 0, "size of log buffers in kilobytes");
   71 
   72 /*
   73  * kern.hwpmc.nbuffer -- number of global log buffers
   74  */
   75 
   76 static int pmc_nlogbuffers = PMC_NLOGBUFFERS;
   77 TUNABLE_INT(PMC_SYSCTL_NAME_PREFIX "nbuffers", &pmc_nlogbuffers);
   78 SYSCTL_INT(_kern_hwpmc, OID_AUTO, nbuffers, CTLFLAG_TUN|CTLFLAG_RD,
   79     &pmc_nlogbuffers, 0, "number of global log buffers");
   80 
   81 /*
   82  * Global log buffer list and associated spin lock.
   83  */
   84 
   85 TAILQ_HEAD(, pmclog_buffer) pmc_bufferlist =
   86         TAILQ_HEAD_INITIALIZER(pmc_bufferlist);
   87 static struct mtx pmc_bufferlist_mtx;   /* spin lock */
   88 static struct mtx pmc_kthread_mtx;      /* sleep lock */
   89 
   90 #define PMCLOG_INIT_BUFFER_DESCRIPTOR(D) do {                           \
   91                 const int __roundup = roundup(sizeof(*D),               \
   92                         sizeof(uint32_t));                              \
   93                 (D)->plb_fence = ((char *) (D)) +                       \
   94                          1024*pmclog_buffer_size;                       \
   95                 (D)->plb_base  = (D)->plb_ptr = ((char *) (D)) +        \
   96                         __roundup;                                      \
   97         } while (0)
   98 
   99 
  100 /*
  101  * Log file record constructors.
  102  */
  103 #define _PMCLOG_TO_HEADER(T,L)                                          \
  104         ((PMCLOG_HEADER_MAGIC << 24) |                                  \
  105          (PMCLOG_TYPE_ ## T << 16)   |                                  \
  106          ((L) & 0xFFFF))
  107 
  108 /* reserve LEN bytes of space and initialize the entry header */
  109 #define _PMCLOG_RESERVE(PO,TYPE,LEN,ACTION) do {                        \
  110                 uint32_t *_le;                                          \
  111                 int _len = roundup((LEN), sizeof(uint32_t));            \
  112                 if ((_le = pmclog_reserve((PO), _len)) == NULL) {       \
  113                         ACTION;                                         \
  114                 }                                                       \
  115                 *_le = _PMCLOG_TO_HEADER(TYPE,_len);                    \
  116                 _le += 3        /* skip over timestamp */
  117 
  118 #define PMCLOG_RESERVE(P,T,L)           _PMCLOG_RESERVE(P,T,L,return)
  119 #define PMCLOG_RESERVE_WITH_ERROR(P,T,L) _PMCLOG_RESERVE(P,T,L,         \
  120         error=ENOMEM;goto error)
  121 
  122 #define PMCLOG_EMIT32(V)        do { *_le++ = (V); } while (0)
  123 #define PMCLOG_EMIT64(V)        do {                                    \
  124                 *_le++ = (uint32_t) ((V) & 0xFFFFFFFF);                 \
  125                 *_le++ = (uint32_t) (((V) >> 32) & 0xFFFFFFFF);         \
  126         } while (0)
  127 
  128 
  129 /* Emit a string.  Caution: does NOT update _le, so needs to be last */
  130 #define PMCLOG_EMITSTRING(S,L)  do { bcopy((S), _le, (L)); } while (0)
  131 
  132 #define PMCLOG_DESPATCH(PO)                                             \
  133                 pmclog_release((PO));                                   \
  134         } while (0)
  135 
  136 
  137 /*
  138  * Assertions about the log file format.
  139  */
  140 
  141 CTASSERT(sizeof(struct pmclog_callchain) == 6*4 +
  142     PMC_CALLCHAIN_DEPTH_MAX*sizeof(uintfptr_t));
  143 CTASSERT(sizeof(struct pmclog_closelog) == 3*4);
  144 CTASSERT(sizeof(struct pmclog_dropnotify) == 3*4);
  145 CTASSERT(sizeof(struct pmclog_map_in) == PATH_MAX +
  146     4*4 + sizeof(uintfptr_t));
  147 CTASSERT(offsetof(struct pmclog_map_in,pl_pathname) ==
  148     4*4 + sizeof(uintfptr_t));
  149 CTASSERT(sizeof(struct pmclog_map_out) == 4*4 + 2*sizeof(uintfptr_t));
  150 CTASSERT(sizeof(struct pmclog_pcsample) == 6*4 + sizeof(uintfptr_t));
  151 CTASSERT(sizeof(struct pmclog_pmcallocate) == 6*4);
  152 CTASSERT(sizeof(struct pmclog_pmcattach) == 5*4 + PATH_MAX);
  153 CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 5*4);
  154 CTASSERT(sizeof(struct pmclog_pmcdetach) == 5*4);
  155 CTASSERT(sizeof(struct pmclog_proccsw) == 5*4 + 8);
  156 CTASSERT(sizeof(struct pmclog_procexec) == 5*4 + PATH_MAX +
  157     sizeof(uintfptr_t));
  158 CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 5*4 +
  159     sizeof(uintfptr_t));
  160 CTASSERT(sizeof(struct pmclog_procexit) == 5*4 + 8);
  161 CTASSERT(sizeof(struct pmclog_procfork) == 5*4);
  162 CTASSERT(sizeof(struct pmclog_sysexit) == 4*4);
  163 CTASSERT(sizeof(struct pmclog_userdata) == 4*4);
  164 
  165 /*
  166  * Log buffer structure
  167  */
  168 
  169 struct pmclog_buffer {
  170         TAILQ_ENTRY(pmclog_buffer) plb_next;
  171         char            *plb_base;
  172         char            *plb_ptr;
  173         char            *plb_fence;
  174 };
  175 
  176 /*
  177  * Prototypes
  178  */
  179 
  180 static int pmclog_get_buffer(struct pmc_owner *po);
  181 static void pmclog_loop(void *arg);
  182 static void pmclog_release(struct pmc_owner *po);
  183 static uint32_t *pmclog_reserve(struct pmc_owner *po, int length);
  184 static void pmclog_schedule_io(struct pmc_owner *po);
  185 static void pmclog_stop_kthread(struct pmc_owner *po);
  186 
  187 /*
  188  * Helper functions
  189  */
  190 
  191 /*
  192  * Get a log buffer
  193  */
  194 
  195 static int
  196 pmclog_get_buffer(struct pmc_owner *po)
  197 {
  198         struct pmclog_buffer *plb;
  199 
  200         mtx_assert(&po->po_mtx, MA_OWNED);
  201 
  202         KASSERT(po->po_curbuf == NULL,
  203             ("[pmclog,%d] po=%p current buffer still valid", __LINE__, po));
  204 
  205         mtx_lock_spin(&pmc_bufferlist_mtx);
  206         if ((plb = TAILQ_FIRST(&pmc_bufferlist)) != NULL)
  207                 TAILQ_REMOVE(&pmc_bufferlist, plb, plb_next);
  208         mtx_unlock_spin(&pmc_bufferlist_mtx);
  209 
  210         PMCDBG(LOG,GTB,1, "po=%p plb=%p", po, plb);
  211 
  212 #ifdef  DEBUG
  213         if (plb)
  214                 KASSERT(plb->plb_ptr == plb->plb_base &&
  215                     plb->plb_base < plb->plb_fence,
  216                     ("[pmclog,%d] po=%p buffer invariants: ptr=%p "
  217                     "base=%p fence=%p", __LINE__, po, plb->plb_ptr,
  218                     plb->plb_base, plb->plb_fence));
  219 #endif
  220 
  221         po->po_curbuf = plb;
  222 
  223         /* update stats */
  224         atomic_add_int(&pmc_stats.pm_buffer_requests, 1);
  225         if (plb == NULL)
  226                 atomic_add_int(&pmc_stats.pm_buffer_requests_failed, 1);
  227 
  228         return (plb ? 0 : ENOMEM);
  229 }
  230 
  231 /*
  232  * Log handler loop.
  233  *
  234  * This function is executed by each pmc owner's helper thread.
  235  */
  236 
  237 static void
  238 pmclog_loop(void *arg)
  239 {
  240         int error;
  241         struct pmc_owner *po;
  242         struct pmclog_buffer *lb;
  243         struct proc *p;
  244         struct ucred *ownercred;
  245         struct ucred *mycred;
  246         struct thread *td;
  247         struct uio auio;
  248         struct iovec aiov;
  249         size_t nbytes;
  250 
  251         po = (struct pmc_owner *) arg;
  252         p = po->po_owner;
  253         td = curthread;
  254         mycred = td->td_ucred;
  255 
  256         PROC_LOCK(p);
  257         ownercred = crhold(p->p_ucred);
  258         PROC_UNLOCK(p);
  259 
  260         PMCDBG(LOG,INI,1, "po=%p kt=%p", po, po->po_kthread);
  261         KASSERT(po->po_kthread == curthread->td_proc,
  262             ("[pmclog,%d] proc mismatch po=%p po/kt=%p curproc=%p", __LINE__,
  263                 po, po->po_kthread, curthread->td_proc));
  264 
  265         lb = NULL;
  266 
  267 
  268         /*
  269          * Loop waiting for I/O requests to be added to the owner
  270          * struct's queue.  The loop is exited when the log file
  271          * is deconfigured.
  272          */
  273 
  274         mtx_lock(&pmc_kthread_mtx);
  275 
  276         for (;;) {
  277 
  278                 /* check if we've been asked to exit */
  279                 if ((po->po_flags & PMC_PO_OWNS_LOGFILE) == 0)
  280                         break;
  281 
  282                 if (lb == NULL) { /* look for a fresh buffer to write */
  283                         mtx_lock_spin(&po->po_mtx);
  284                         if ((lb = TAILQ_FIRST(&po->po_logbuffers)) == NULL) {
  285                                 mtx_unlock_spin(&po->po_mtx);
  286 
  287                                 /*
  288                                  * Wakeup the thread waiting for the
  289                                  * PMC_OP_FLUSHLOG request to
  290                                  * complete.
  291                                  */
  292                                 if (po->po_flags & PMC_PO_IN_FLUSH) {
  293                                         po->po_flags &= ~PMC_PO_IN_FLUSH;
  294                                         wakeup_one(po->po_kthread);
  295                                 }
  296 
  297                                 (void) msleep(po, &pmc_kthread_mtx, PWAIT,
  298                                     "pmcloop", 0);
  299                                 continue;
  300                         }
  301 
  302                         TAILQ_REMOVE(&po->po_logbuffers, lb, plb_next);
  303                         mtx_unlock_spin(&po->po_mtx);
  304                 }
  305 
  306                 mtx_unlock(&pmc_kthread_mtx);
  307 
  308                 /* process the request */
  309                 PMCDBG(LOG,WRI,2, "po=%p base=%p ptr=%p", po,
  310                     lb->plb_base, lb->plb_ptr);
  311                 /* change our thread's credentials before issuing the I/O */
  312 
  313                 aiov.iov_base = lb->plb_base;
  314                 aiov.iov_len  = nbytes = lb->plb_ptr - lb->plb_base;
  315 
  316                 auio.uio_iov    = &aiov;
  317                 auio.uio_iovcnt = 1;
  318                 auio.uio_offset = -1;
  319                 auio.uio_resid  = nbytes;
  320                 auio.uio_rw     = UIO_WRITE;
  321                 auio.uio_segflg = UIO_SYSSPACE;
  322                 auio.uio_td     = td;
  323 
  324                 /* switch thread credentials -- see kern_ktrace.c */
  325                 td->td_ucred = ownercred;
  326                 error = fo_write(po->po_file, &auio, ownercred, 0, td);
  327                 td->td_ucred = mycred;
  328 
  329                 if (error) {
  330                         /* XXX some errors are recoverable */
  331                         /* XXX also check for SIGPIPE if a socket */
  332 
  333                         /* send a SIGIO to the owner and exit */
  334                         PROC_LOCK(p);
  335                         psignal(p, SIGIO);
  336                         PROC_UNLOCK(p);
  337 
  338                         mtx_lock(&pmc_kthread_mtx);
  339 
  340                         po->po_error = error; /* save for flush log */
  341 
  342                         PMCDBG(LOG,WRI,2, "po=%p error=%d", po, error);
  343 
  344                         break;
  345                 }
  346 
  347                 mtx_lock(&pmc_kthread_mtx);
  348 
  349                 /* put the used buffer back into the global pool */
  350                 PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
  351 
  352                 mtx_lock_spin(&pmc_bufferlist_mtx);
  353                 TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
  354                 mtx_unlock_spin(&pmc_bufferlist_mtx);
  355 
  356                 lb = NULL;
  357         }
  358 
  359         po->po_kthread = NULL;
  360 
  361         mtx_unlock(&pmc_kthread_mtx);
  362 
  363         /* return the current I/O buffer to the global pool */
  364         if (lb) {
  365                 PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
  366 
  367                 mtx_lock_spin(&pmc_bufferlist_mtx);
  368                 TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
  369                 mtx_unlock_spin(&pmc_bufferlist_mtx);
  370         }
  371 
  372         /*
  373          * Exit this thread, signalling the waiter
  374          */
  375 
  376         crfree(ownercred);
  377 
  378         kthread_exit(0);
  379 }
  380 
  381 /*
  382  * Release and log entry and schedule an I/O if needed.
  383  */
  384 
  385 static void
  386 pmclog_release(struct pmc_owner *po)
  387 {
  388         KASSERT(po->po_curbuf->plb_ptr >= po->po_curbuf->plb_base,
  389             ("[pmclog,%d] buffer invariants po=%p ptr=%p base=%p", __LINE__,
  390                 po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base));
  391         KASSERT(po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
  392             ("[pmclog,%d] buffer invariants po=%p ptr=%p fenc=%p", __LINE__,
  393                 po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_fence));
  394 
  395         /* schedule an I/O if we've filled a buffer */
  396         if (po->po_curbuf->plb_ptr >= po->po_curbuf->plb_fence)
  397                 pmclog_schedule_io(po);
  398 
  399         mtx_unlock_spin(&po->po_mtx);
  400 
  401         PMCDBG(LOG,REL,1, "po=%p", po);
  402 }
  403 
  404 
  405 /*
  406  * Attempt to reserve 'length' bytes of space in an owner's log
  407  * buffer.  The function returns a pointer to 'length' bytes of space
  408  * if there was enough space or returns NULL if no space was
  409  * available.  Non-null returns do so with the po mutex locked.  The
  410  * caller must invoke pmclog_release() on the pmc owner structure
  411  * when done.
  412  */
  413 
  414 static uint32_t *
  415 pmclog_reserve(struct pmc_owner *po, int length)
  416 {
  417         uintptr_t newptr, oldptr;
  418         uint32_t *lh;
  419         struct timespec ts;
  420 
  421         PMCDBG(LOG,ALL,1, "po=%p len=%d", po, length);
  422 
  423         KASSERT(length % sizeof(uint32_t) == 0,
  424             ("[pmclog,%d] length not a multiple of word size", __LINE__));
  425 
  426         mtx_lock_spin(&po->po_mtx);
  427 
  428         if (po->po_curbuf == NULL)
  429                 if (pmclog_get_buffer(po) != 0) {
  430                         mtx_unlock_spin(&po->po_mtx);
  431                         return (NULL);
  432                 }
  433 
  434         KASSERT(po->po_curbuf != NULL,
  435             ("[pmclog,%d] po=%p no current buffer", __LINE__, po));
  436 
  437         KASSERT(po->po_curbuf->plb_ptr >= po->po_curbuf->plb_base &&
  438             po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
  439             ("[pmclog,%d] po=%p buffer invariants: ptr=%p base=%p fence=%p",
  440                 __LINE__, po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base,
  441                 po->po_curbuf->plb_fence));
  442 
  443         oldptr = (uintptr_t) po->po_curbuf->plb_ptr;
  444         newptr = oldptr + length;
  445 
  446         KASSERT(oldptr != (uintptr_t) NULL,
  447             ("[pmclog,%d] po=%p Null log buffer pointer", __LINE__, po));
  448 
  449         /*
  450          * If we have space in the current buffer, return a pointer to
  451          * available space with the PO structure locked.
  452          */
  453         if (newptr <= (uintptr_t) po->po_curbuf->plb_fence) {
  454                 po->po_curbuf->plb_ptr = (char *) newptr;
  455                 goto done;
  456         }
  457 
  458         /*
  459          * Otherwise, schedule the current buffer for output and get a
  460          * fresh buffer.
  461          */
  462         pmclog_schedule_io(po);
  463 
  464         if (pmclog_get_buffer(po) != 0) {
  465                 mtx_unlock_spin(&po->po_mtx);
  466                 return (NULL);
  467         }
  468 
  469         KASSERT(po->po_curbuf != NULL,
  470             ("[pmclog,%d] po=%p no current buffer", __LINE__, po));
  471 
  472         KASSERT(po->po_curbuf->plb_ptr != NULL,
  473             ("[pmclog,%d] null return from pmc_get_log_buffer", __LINE__));
  474 
  475         KASSERT(po->po_curbuf->plb_ptr == po->po_curbuf->plb_base &&
  476             po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
  477             ("[pmclog,%d] po=%p buffer invariants: ptr=%p base=%p fence=%p",
  478                 __LINE__, po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base,
  479                 po->po_curbuf->plb_fence));
  480 
  481         oldptr = (uintptr_t) po->po_curbuf->plb_ptr;
  482 
  483  done:
  484         lh = (uint32_t *) oldptr;
  485         lh++;                           /* skip header */
  486         getnanotime(&ts);               /* fill in the timestamp */
  487         *lh++ = ts.tv_sec & 0xFFFFFFFF;
  488         *lh++ = ts.tv_nsec & 0xFFFFFFF;
  489         return ((uint32_t *) oldptr);
  490 }
  491 
  492 /*
  493  * Schedule an I/O.
  494  *
  495  * Transfer the current buffer to the helper kthread.
  496  */
  497 
  498 static void
  499 pmclog_schedule_io(struct pmc_owner *po)
  500 {
  501         KASSERT(po->po_curbuf != NULL,
  502             ("[pmclog,%d] schedule_io with null buffer po=%p", __LINE__, po));
  503 
  504         KASSERT(po->po_curbuf->plb_ptr >= po->po_curbuf->plb_base,
  505             ("[pmclog,%d] buffer invariants po=%p ptr=%p base=%p", __LINE__,
  506                 po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_base));
  507         KASSERT(po->po_curbuf->plb_ptr <= po->po_curbuf->plb_fence,
  508             ("[pmclog,%d] buffer invariants po=%p ptr=%p fenc=%p", __LINE__,
  509                 po, po->po_curbuf->plb_ptr, po->po_curbuf->plb_fence));
  510 
  511         PMCDBG(LOG,SIO, 1, "po=%p", po);
  512 
  513         mtx_assert(&po->po_mtx, MA_OWNED);
  514 
  515         /*
  516          * Add the current buffer to the tail of the buffer list and
  517          * wakeup the helper.
  518          */
  519         TAILQ_INSERT_TAIL(&po->po_logbuffers, po->po_curbuf, plb_next);
  520         po->po_curbuf = NULL;
  521         wakeup_one(po);
  522 }
  523 
  524 /*
  525  * Stop the helper kthread.
  526  */
  527 
  528 static void
  529 pmclog_stop_kthread(struct pmc_owner *po)
  530 {
  531         /*
  532          * Close the file to force the thread out of fo_write,
  533          * unset flag, wakeup the helper thread,
  534          * wait for it to exit
  535          */
  536 
  537         if (po->po_file != NULL)
  538                 fo_close(po->po_file, curthread);
  539 
  540         mtx_lock(&pmc_kthread_mtx);
  541         po->po_flags &= ~PMC_PO_OWNS_LOGFILE;
  542         wakeup_one(po);
  543         if (po->po_kthread)
  544                 msleep(po->po_kthread, &pmc_kthread_mtx, PPAUSE, "pmckstp", 0);
  545         mtx_unlock(&pmc_kthread_mtx);
  546 }
  547 
  548 /*
  549  * Public functions
  550  */
  551 
  552 /*
  553  * Configure a log file for pmc owner 'po'.
  554  *
  555  * Parameter 'logfd' is a file handle referencing an open file in the
  556  * owner process.  This file needs to have been opened for writing.
  557  */
  558 
  559 int
  560 pmclog_configure_log(struct pmc_mdep *md, struct pmc_owner *po, int logfd)
  561 {
  562         int error;
  563         struct proc *p;
  564 
  565         /*
  566          * As long as it is possible to get a LOR between pmc_sx lock and
  567          * proctree/allproc sx locks used for adding a new process, assure
  568          * the former is not held here.
  569          */
  570         sx_assert(&pmc_sx, SA_UNLOCKED);
  571         PMCDBG(LOG,CFG,1, "config po=%p logfd=%d", po, logfd);
  572 
  573         p = po->po_owner;
  574 
  575         /* return EBUSY if a log file was already present */
  576         if (po->po_flags & PMC_PO_OWNS_LOGFILE)
  577                 return (EBUSY);
  578 
  579         KASSERT(po->po_kthread == NULL,
  580             ("[pmclog,%d] po=%p kthread (%p) already present", __LINE__, po,
  581                 po->po_kthread));
  582         KASSERT(po->po_file == NULL,
  583             ("[pmclog,%d] po=%p file (%p) already present", __LINE__, po,
  584                 po->po_file));
  585 
  586         /* get a reference to the file state */
  587         error = fget_write(curthread, logfd, &po->po_file);
  588         if (error)
  589                 goto error;
  590 
  591         /* mark process as owning a log file */
  592         po->po_flags |= PMC_PO_OWNS_LOGFILE;
  593         error = kthread_create(pmclog_loop, po, &po->po_kthread,
  594             RFHIGHPID, 0, "hwpmc: proc(%d)", p->p_pid);
  595         if (error)
  596                 goto error;
  597 
  598         /* mark process as using HWPMCs */
  599         PROC_LOCK(p);
  600         p->p_flag |= P_HWPMC;
  601         PROC_UNLOCK(p);
  602 
  603         /* create a log initialization entry */
  604         PMCLOG_RESERVE_WITH_ERROR(po, INITIALIZE,
  605             sizeof(struct pmclog_initialize));
  606         PMCLOG_EMIT32(PMC_VERSION);
  607         PMCLOG_EMIT32(md->pmd_cputype);
  608         PMCLOG_DESPATCH(po);
  609 
  610         return (0);
  611 
  612  error:
  613         /* shutdown the thread */
  614         if (po->po_kthread)
  615                 pmclog_stop_kthread(po);
  616 
  617         KASSERT(po->po_kthread == NULL, ("[pmclog,%d] po=%p kthread not "
  618             "stopped", __LINE__, po));
  619 
  620         if (po->po_file)
  621                 (void) fdrop(po->po_file, curthread);
  622         po->po_file  = NULL;    /* clear file and error state */
  623         po->po_error = 0;
  624 
  625         return (error);
  626 }
  627 
  628 
  629 /*
  630  * De-configure a log file.  This will throw away any buffers queued
  631  * for this owner process.
  632  */
  633 
  634 int
  635 pmclog_deconfigure_log(struct pmc_owner *po)
  636 {
  637         int error;
  638         struct pmclog_buffer *lb;
  639 
  640         PMCDBG(LOG,CFG,1, "de-config po=%p", po);
  641 
  642         if ((po->po_flags & PMC_PO_OWNS_LOGFILE) == 0)
  643                 return (EINVAL);
  644 
  645         KASSERT(po->po_sscount == 0,
  646             ("[pmclog,%d] po=%p still owning SS PMCs", __LINE__, po));
  647         KASSERT(po->po_file != NULL,
  648             ("[pmclog,%d] po=%p no log file", __LINE__, po));
  649 
  650         /* stop the kthread, this will reset the 'OWNS_LOGFILE' flag */
  651         if (po->po_kthread)
  652                 pmclog_stop_kthread(po);
  653 
  654         KASSERT(po->po_kthread == NULL,
  655             ("[pmclog,%d] po=%p kthread not stopped", __LINE__, po));
  656 
  657         /* return all queued log buffers to the global pool */
  658         while ((lb = TAILQ_FIRST(&po->po_logbuffers)) != NULL) {
  659                 TAILQ_REMOVE(&po->po_logbuffers, lb, plb_next);
  660                 PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
  661                 mtx_lock_spin(&pmc_bufferlist_mtx);
  662                 TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
  663                 mtx_unlock_spin(&pmc_bufferlist_mtx);
  664         }
  665 
  666         /* return the 'current' buffer to the global pool */
  667         if ((lb = po->po_curbuf) != NULL) {
  668                 PMCLOG_INIT_BUFFER_DESCRIPTOR(lb);
  669                 mtx_lock_spin(&pmc_bufferlist_mtx);
  670                 TAILQ_INSERT_HEAD(&pmc_bufferlist, lb, plb_next);
  671                 mtx_unlock_spin(&pmc_bufferlist_mtx);
  672         }
  673 
  674         /* drop a reference to the fd */
  675         error = fdrop(po->po_file, curthread);
  676         po->po_file  = NULL;
  677         po->po_error = 0;
  678 
  679         return (error);
  680 }
  681 
  682 /*
  683  * Flush a process' log buffer.
  684  */
  685 
  686 int
  687 pmclog_flush(struct pmc_owner *po)
  688 {
  689         int error, has_pending_buffers;
  690 
  691         PMCDBG(LOG,FLS,1, "po=%p", po);
  692 
  693         /*
  694          * If there is a pending error recorded by the logger thread,
  695          * return that.
  696          */
  697         if (po->po_error)
  698                 return (po->po_error);
  699 
  700         error = 0;
  701 
  702         /*
  703          * Check that we do have an active log file.
  704          */
  705         mtx_lock(&pmc_kthread_mtx);
  706         if ((po->po_flags & PMC_PO_OWNS_LOGFILE) == 0) {
  707                 error = EINVAL;
  708                 goto error;
  709         }
  710 
  711         /*
  712          * Schedule the current buffer if any.
  713          */
  714         mtx_lock_spin(&po->po_mtx);
  715         if (po->po_curbuf)
  716                 pmclog_schedule_io(po);
  717         has_pending_buffers = !TAILQ_EMPTY(&po->po_logbuffers);
  718         mtx_unlock_spin(&po->po_mtx);
  719 
  720         if (has_pending_buffers) {
  721                 po->po_flags |= PMC_PO_IN_FLUSH; /* ask for a wakeup */
  722                 error = msleep(po->po_kthread, &pmc_kthread_mtx, PWAIT,
  723                     "pmcflush", 0);
  724                 if (error == 0)
  725                         error = po->po_error;
  726         }
  727 
  728  error:
  729         mtx_unlock(&pmc_kthread_mtx);
  730 
  731         return (error);
  732 }
  733 
  734 
  735 void
  736 pmclog_process_callchain(struct pmc *pm, struct pmc_sample *ps)
  737 {
  738         int n, recordlen;
  739         uint32_t flags;
  740         struct pmc_owner *po;
  741 
  742         PMCDBG(LOG,SAM,1,"pm=%p pid=%d n=%d", pm, ps->ps_pid,
  743             ps->ps_nsamples);
  744 
  745         recordlen = offsetof(struct pmclog_callchain, pl_pc) +
  746             ps->ps_nsamples * sizeof(uintfptr_t);
  747         po = pm->pm_owner;
  748         flags = PMC_CALLCHAIN_TO_CPUFLAGS(ps->ps_cpu,ps->ps_flags);
  749         PMCLOG_RESERVE(po, CALLCHAIN, recordlen);
  750         PMCLOG_EMIT32(ps->ps_pid);
  751         PMCLOG_EMIT32(pm->pm_id);
  752         PMCLOG_EMIT32(flags);
  753         for (n = 0; n < ps->ps_nsamples; n++)
  754                 PMCLOG_EMITADDR(ps->ps_pc[n]);
  755         PMCLOG_DESPATCH(po);
  756 }
  757 
  758 void
  759 pmclog_process_closelog(struct pmc_owner *po)
  760 {
  761         PMCLOG_RESERVE(po,CLOSELOG,sizeof(struct pmclog_closelog));
  762         PMCLOG_DESPATCH(po);
  763 }
  764 
  765 void
  766 pmclog_process_dropnotify(struct pmc_owner *po)
  767 {
  768         PMCLOG_RESERVE(po,DROPNOTIFY,sizeof(struct pmclog_dropnotify));
  769         PMCLOG_DESPATCH(po);
  770 }
  771 
  772 void
  773 pmclog_process_map_in(struct pmc_owner *po, pid_t pid, uintfptr_t start,
  774     const char *path)
  775 {
  776         int pathlen, recordlen;
  777 
  778         KASSERT(path != NULL, ("[pmclog,%d] map-in, null path", __LINE__));
  779 
  780         pathlen = strlen(path) + 1;     /* #bytes for path name */
  781         recordlen = offsetof(struct pmclog_map_in, pl_pathname) +
  782             pathlen;
  783 
  784         PMCLOG_RESERVE(po, MAP_IN, recordlen);
  785         PMCLOG_EMIT32(pid);
  786         PMCLOG_EMITADDR(start);
  787         PMCLOG_EMITSTRING(path,pathlen);
  788         PMCLOG_DESPATCH(po);
  789 }
  790 
  791 void
  792 pmclog_process_map_out(struct pmc_owner *po, pid_t pid, uintfptr_t start,
  793     uintfptr_t end)
  794 {
  795         KASSERT(start <= end, ("[pmclog,%d] start > end", __LINE__));
  796 
  797         PMCLOG_RESERVE(po, MAP_OUT, sizeof(struct pmclog_map_out));
  798         PMCLOG_EMIT32(pid);
  799         PMCLOG_EMITADDR(start);
  800         PMCLOG_EMITADDR(end);
  801         PMCLOG_DESPATCH(po);
  802 }
  803 
  804 void
  805 pmclog_process_pmcallocate(struct pmc *pm)
  806 {
  807         struct pmc_owner *po;
  808 
  809         po = pm->pm_owner;
  810 
  811         PMCDBG(LOG,ALL,1, "pm=%p", pm);
  812 
  813         PMCLOG_RESERVE(po, PMCALLOCATE, sizeof(struct pmclog_pmcallocate));
  814         PMCLOG_EMIT32(pm->pm_id);
  815         PMCLOG_EMIT32(pm->pm_event);
  816         PMCLOG_EMIT32(pm->pm_flags);
  817         PMCLOG_DESPATCH(po);
  818 }
  819 
  820 void
  821 pmclog_process_pmcattach(struct pmc *pm, pid_t pid, char *path)
  822 {
  823         int pathlen, recordlen;
  824         struct pmc_owner *po;
  825 
  826         PMCDBG(LOG,ATT,1,"pm=%p pid=%d", pm, pid);
  827 
  828         po = pm->pm_owner;
  829 
  830         pathlen = strlen(path) + 1;     /* #bytes for the string */
  831         recordlen = offsetof(struct pmclog_pmcattach, pl_pathname) + pathlen;
  832 
  833         PMCLOG_RESERVE(po, PMCATTACH, recordlen);
  834         PMCLOG_EMIT32(pm->pm_id);
  835         PMCLOG_EMIT32(pid);
  836         PMCLOG_EMITSTRING(path, pathlen);
  837         PMCLOG_DESPATCH(po);
  838 }
  839 
  840 void
  841 pmclog_process_pmcdetach(struct pmc *pm, pid_t pid)
  842 {
  843         struct pmc_owner *po;
  844 
  845         PMCDBG(LOG,ATT,1,"!pm=%p pid=%d", pm, pid);
  846 
  847         po = pm->pm_owner;
  848 
  849         PMCLOG_RESERVE(po, PMCDETACH, sizeof(struct pmclog_pmcdetach));
  850         PMCLOG_EMIT32(pm->pm_id);
  851         PMCLOG_EMIT32(pid);
  852         PMCLOG_DESPATCH(po);
  853 }
  854 
  855 /*
  856  * Log a context switch event to the log file.
  857  */
  858 
  859 void
  860 pmclog_process_proccsw(struct pmc *pm, struct pmc_process *pp, pmc_value_t v)
  861 {
  862         struct pmc_owner *po;
  863 
  864         KASSERT(pm->pm_flags & PMC_F_LOG_PROCCSW,
  865             ("[pmclog,%d] log-process-csw called gratuitously", __LINE__));
  866 
  867         PMCDBG(LOG,SWO,1,"pm=%p pid=%d v=%jx", pm, pp->pp_proc->p_pid,
  868             v);
  869 
  870         po = pm->pm_owner;
  871 
  872         PMCLOG_RESERVE(po, PROCCSW, sizeof(struct pmclog_proccsw));
  873         PMCLOG_EMIT32(pm->pm_id);
  874         PMCLOG_EMIT64(v);
  875         PMCLOG_EMIT32(pp->pp_proc->p_pid);
  876         PMCLOG_DESPATCH(po);
  877 }
  878 
  879 void
  880 pmclog_process_procexec(struct pmc_owner *po, pmc_id_t pmid, pid_t pid,
  881     uintfptr_t startaddr, char *path)
  882 {
  883         int pathlen, recordlen;
  884 
  885         PMCDBG(LOG,EXC,1,"po=%p pid=%d path=\"%s\"", po, pid, path);
  886 
  887         pathlen   = strlen(path) + 1;   /* #bytes for the path */
  888         recordlen = offsetof(struct pmclog_procexec, pl_pathname) + pathlen;
  889 
  890         PMCLOG_RESERVE(po, PROCEXEC, recordlen);
  891         PMCLOG_EMIT32(pid);
  892         PMCLOG_EMITADDR(startaddr);
  893         PMCLOG_EMIT32(pmid);
  894         PMCLOG_EMITSTRING(path,pathlen);
  895         PMCLOG_DESPATCH(po);
  896 }
  897 
  898 /*
  899  * Log a process exit event (and accumulated pmc value) to the log file.
  900  */
  901 
  902 void
  903 pmclog_process_procexit(struct pmc *pm, struct pmc_process *pp)
  904 {
  905         int ri;
  906         struct pmc_owner *po;
  907 
  908         ri = PMC_TO_ROWINDEX(pm);
  909         PMCDBG(LOG,EXT,1,"pm=%p pid=%d v=%jx", pm, pp->pp_proc->p_pid,
  910             pp->pp_pmcs[ri].pp_pmcval);
  911 
  912         po = pm->pm_owner;
  913 
  914         PMCLOG_RESERVE(po, PROCEXIT, sizeof(struct pmclog_procexit));
  915         PMCLOG_EMIT32(pm->pm_id);
  916         PMCLOG_EMIT64(pp->pp_pmcs[ri].pp_pmcval);
  917         PMCLOG_EMIT32(pp->pp_proc->p_pid);
  918         PMCLOG_DESPATCH(po);
  919 }
  920 
  921 /*
  922  * Log a fork event.
  923  */
  924 
  925 void
  926 pmclog_process_procfork(struct pmc_owner *po, pid_t oldpid, pid_t newpid)
  927 {
  928         PMCLOG_RESERVE(po, PROCFORK, sizeof(struct pmclog_procfork));
  929         PMCLOG_EMIT32(oldpid);
  930         PMCLOG_EMIT32(newpid);
  931         PMCLOG_DESPATCH(po);
  932 }
  933 
  934 /*
  935  * Log a process exit event of the form suitable for system-wide PMCs.
  936  */
  937 
  938 void
  939 pmclog_process_sysexit(struct pmc_owner *po, pid_t pid)
  940 {
  941         PMCLOG_RESERVE(po, SYSEXIT, sizeof(struct pmclog_sysexit));
  942         PMCLOG_EMIT32(pid);
  943         PMCLOG_DESPATCH(po);
  944 }
  945 
  946 /*
  947  * Write a user log entry.
  948  */
  949 
  950 int
  951 pmclog_process_userlog(struct pmc_owner *po, struct pmc_op_writelog *wl)
  952 {
  953         int error;
  954 
  955         PMCDBG(LOG,WRI,1, "writelog po=%p ud=0x%x", po, wl->pm_userdata);
  956 
  957         error = 0;
  958 
  959         PMCLOG_RESERVE_WITH_ERROR(po, USERDATA,
  960             sizeof(struct pmclog_userdata));
  961         PMCLOG_EMIT32(wl->pm_userdata);
  962         PMCLOG_DESPATCH(po);
  963 
  964  error:
  965         return (error);
  966 }
  967 
  968 /*
  969  * Initialization.
  970  *
  971  * Create a pool of log buffers and initialize mutexes.
  972  */
  973 
  974 void
  975 pmclog_initialize()
  976 {
  977         int n;
  978         struct pmclog_buffer *plb;
  979 
  980         if (pmclog_buffer_size <= 0) {
  981                 (void) printf("hwpmc: tunable logbuffersize=%d must be "
  982                     "greater than zero.\n", pmclog_buffer_size);
  983                 pmclog_buffer_size = PMC_LOG_BUFFER_SIZE;
  984         }
  985 
  986         if (pmc_nlogbuffers <= 0) {
  987                 (void) printf("hwpmc: tunable nlogbuffers=%d must be greater "
  988                     "than zero.\n", pmc_nlogbuffers);
  989                 pmc_nlogbuffers = PMC_NLOGBUFFERS;
  990         }
  991 
  992         /* create global pool of log buffers */
  993         for (n = 0; n < pmc_nlogbuffers; n++) {
  994                 plb = malloc(1024 * pmclog_buffer_size, M_PMC,
  995                     M_WAITOK|M_ZERO);
  996                 PMCLOG_INIT_BUFFER_DESCRIPTOR(plb);
  997                 TAILQ_INSERT_HEAD(&pmc_bufferlist, plb, plb_next);
  998         }
  999         mtx_init(&pmc_bufferlist_mtx, "pmc-buffer-list", "pmc-leaf",
 1000             MTX_SPIN);
 1001         mtx_init(&pmc_kthread_mtx, "pmc-kthread", "pmc-sleep", MTX_DEF);
 1002 }
 1003 
 1004 /*
 1005  * Shutdown logging.
 1006  *
 1007  * Destroy mutexes and release memory back the to free pool.
 1008  */
 1009 
 1010 void
 1011 pmclog_shutdown()
 1012 {
 1013         struct pmclog_buffer *plb;
 1014 
 1015         mtx_destroy(&pmc_kthread_mtx);
 1016         mtx_destroy(&pmc_bufferlist_mtx);
 1017 
 1018         while ((plb = TAILQ_FIRST(&pmc_bufferlist)) != NULL) {
 1019                 TAILQ_REMOVE(&pmc_bufferlist, plb, plb_next);
 1020                 FREE(plb, M_PMC);
 1021         }
 1022 }

Cache object: 480eb92732756c58224b9c8e59663de9


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