332 lines
7.2 KiB
C
332 lines
7.2 KiB
C
/*
|
|
* Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it would be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
#include <xfs.h>
|
|
|
|
static kmem_zone_t *ktrace_hdr_zone;
|
|
static kmem_zone_t *ktrace_ent_zone;
|
|
static int ktrace_zentries;
|
|
|
|
void
|
|
ktrace_init(int zentries)
|
|
{
|
|
ktrace_zentries = zentries;
|
|
|
|
ktrace_hdr_zone = kmem_zone_init(sizeof(ktrace_t),
|
|
"ktrace_hdr");
|
|
ASSERT(ktrace_hdr_zone);
|
|
|
|
ktrace_ent_zone = kmem_zone_init(ktrace_zentries
|
|
* sizeof(ktrace_entry_t),
|
|
"ktrace_ent");
|
|
ASSERT(ktrace_ent_zone);
|
|
}
|
|
|
|
void
|
|
ktrace_uninit(void)
|
|
{
|
|
kmem_zone_destroy(ktrace_hdr_zone);
|
|
kmem_zone_destroy(ktrace_ent_zone);
|
|
}
|
|
|
|
/*
|
|
* ktrace_alloc()
|
|
*
|
|
* Allocate a ktrace header and enough buffering for the given
|
|
* number of entries.
|
|
*/
|
|
ktrace_t *
|
|
ktrace_alloc(int nentries, unsigned int __nocast sleep)
|
|
{
|
|
ktrace_t *ktp;
|
|
ktrace_entry_t *ktep;
|
|
|
|
ktp = (ktrace_t*)kmem_zone_alloc(ktrace_hdr_zone, sleep);
|
|
|
|
if (ktp == (ktrace_t*)NULL) {
|
|
/*
|
|
* KM_SLEEP callers don't expect failure.
|
|
*/
|
|
if (sleep & KM_SLEEP)
|
|
panic("ktrace_alloc: NULL memory on KM_SLEEP request!");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Special treatment for buffers with the ktrace_zentries entries
|
|
*/
|
|
if (nentries == ktrace_zentries) {
|
|
ktep = (ktrace_entry_t*)kmem_zone_zalloc(ktrace_ent_zone,
|
|
sleep);
|
|
} else {
|
|
ktep = (ktrace_entry_t*)kmem_zalloc((nentries * sizeof(*ktep)),
|
|
sleep);
|
|
}
|
|
|
|
if (ktep == NULL) {
|
|
/*
|
|
* KM_SLEEP callers don't expect failure.
|
|
*/
|
|
if (sleep & KM_SLEEP)
|
|
panic("ktrace_alloc: NULL memory on KM_SLEEP request!");
|
|
|
|
kmem_free(ktp, sizeof(*ktp));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
spinlock_init(&(ktp->kt_lock), "kt_lock");
|
|
|
|
ktp->kt_entries = ktep;
|
|
ktp->kt_nentries = nentries;
|
|
ktp->kt_index = 0;
|
|
ktp->kt_rollover = 0;
|
|
return ktp;
|
|
}
|
|
|
|
|
|
/*
|
|
* ktrace_free()
|
|
*
|
|
* Free up the ktrace header and buffer. It is up to the caller
|
|
* to ensure that no-one is referencing it.
|
|
*/
|
|
void
|
|
ktrace_free(ktrace_t *ktp)
|
|
{
|
|
int entries_size;
|
|
|
|
if (ktp == (ktrace_t *)NULL)
|
|
return;
|
|
|
|
spinlock_destroy(&ktp->kt_lock);
|
|
|
|
/*
|
|
* Special treatment for the Vnode trace buffer.
|
|
*/
|
|
if (ktp->kt_nentries == ktrace_zentries) {
|
|
kmem_zone_free(ktrace_ent_zone, ktp->kt_entries);
|
|
} else {
|
|
entries_size = (int)(ktp->kt_nentries * sizeof(ktrace_entry_t));
|
|
|
|
kmem_free(ktp->kt_entries, entries_size);
|
|
}
|
|
|
|
kmem_zone_free(ktrace_hdr_zone, ktp);
|
|
}
|
|
|
|
|
|
/*
|
|
* Enter the given values into the "next" entry in the trace buffer.
|
|
* kt_index is always the index of the next entry to be filled.
|
|
*/
|
|
void
|
|
ktrace_enter(
|
|
ktrace_t *ktp,
|
|
void *val0,
|
|
void *val1,
|
|
void *val2,
|
|
void *val3,
|
|
void *val4,
|
|
void *val5,
|
|
void *val6,
|
|
void *val7,
|
|
void *val8,
|
|
void *val9,
|
|
void *val10,
|
|
void *val11,
|
|
void *val12,
|
|
void *val13,
|
|
void *val14,
|
|
void *val15)
|
|
{
|
|
static DEFINE_SPINLOCK(wrap_lock);
|
|
unsigned long flags;
|
|
int index;
|
|
ktrace_entry_t *ktep;
|
|
|
|
ASSERT(ktp != NULL);
|
|
|
|
/*
|
|
* Grab an entry by pushing the index up to the next one.
|
|
*/
|
|
spin_lock_irqsave(&wrap_lock, flags);
|
|
index = ktp->kt_index;
|
|
if (++ktp->kt_index == ktp->kt_nentries)
|
|
ktp->kt_index = 0;
|
|
spin_unlock_irqrestore(&wrap_lock, flags);
|
|
|
|
if (!ktp->kt_rollover && index == ktp->kt_nentries - 1)
|
|
ktp->kt_rollover = 1;
|
|
|
|
ASSERT((index >= 0) && (index < ktp->kt_nentries));
|
|
|
|
ktep = &(ktp->kt_entries[index]);
|
|
|
|
ktep->val[0] = val0;
|
|
ktep->val[1] = val1;
|
|
ktep->val[2] = val2;
|
|
ktep->val[3] = val3;
|
|
ktep->val[4] = val4;
|
|
ktep->val[5] = val5;
|
|
ktep->val[6] = val6;
|
|
ktep->val[7] = val7;
|
|
ktep->val[8] = val8;
|
|
ktep->val[9] = val9;
|
|
ktep->val[10] = val10;
|
|
ktep->val[11] = val11;
|
|
ktep->val[12] = val12;
|
|
ktep->val[13] = val13;
|
|
ktep->val[14] = val14;
|
|
ktep->val[15] = val15;
|
|
}
|
|
|
|
/*
|
|
* Return the number of entries in the trace buffer.
|
|
*/
|
|
int
|
|
ktrace_nentries(
|
|
ktrace_t *ktp)
|
|
{
|
|
if (ktp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return (ktp->kt_rollover ? ktp->kt_nentries : ktp->kt_index);
|
|
}
|
|
|
|
/*
|
|
* ktrace_first()
|
|
*
|
|
* This is used to find the start of the trace buffer.
|
|
* In conjunction with ktrace_next() it can be used to
|
|
* iterate through the entire trace buffer. This code does
|
|
* not do any locking because it is assumed that it is called
|
|
* from the debugger.
|
|
*
|
|
* The caller must pass in a pointer to a ktrace_snap
|
|
* structure in which we will keep some state used to
|
|
* iterate through the buffer. This state must not touched
|
|
* by any code outside of this module.
|
|
*/
|
|
ktrace_entry_t *
|
|
ktrace_first(ktrace_t *ktp, ktrace_snap_t *ktsp)
|
|
{
|
|
ktrace_entry_t *ktep;
|
|
int index;
|
|
int nentries;
|
|
|
|
if (ktp->kt_rollover)
|
|
index = ktp->kt_index;
|
|
else
|
|
index = 0;
|
|
|
|
ktsp->ks_start = index;
|
|
ktep = &(ktp->kt_entries[index]);
|
|
|
|
nentries = ktrace_nentries(ktp);
|
|
index++;
|
|
if (index < nentries) {
|
|
ktsp->ks_index = index;
|
|
} else {
|
|
ktsp->ks_index = 0;
|
|
if (index > nentries)
|
|
ktep = NULL;
|
|
}
|
|
return ktep;
|
|
}
|
|
|
|
/*
|
|
* ktrace_next()
|
|
*
|
|
* This is used to iterate through the entries of the given
|
|
* trace buffer. The caller must pass in the ktrace_snap_t
|
|
* structure initialized by ktrace_first(). The return value
|
|
* will be either a pointer to the next ktrace_entry or NULL
|
|
* if all of the entries have been traversed.
|
|
*/
|
|
ktrace_entry_t *
|
|
ktrace_next(
|
|
ktrace_t *ktp,
|
|
ktrace_snap_t *ktsp)
|
|
{
|
|
int index;
|
|
ktrace_entry_t *ktep;
|
|
|
|
index = ktsp->ks_index;
|
|
if (index == ktsp->ks_start) {
|
|
ktep = NULL;
|
|
} else {
|
|
ktep = &ktp->kt_entries[index];
|
|
}
|
|
|
|
index++;
|
|
if (index == ktrace_nentries(ktp)) {
|
|
ktsp->ks_index = 0;
|
|
} else {
|
|
ktsp->ks_index = index;
|
|
}
|
|
|
|
return ktep;
|
|
}
|
|
|
|
/*
|
|
* ktrace_skip()
|
|
*
|
|
* Skip the next "count" entries and return the entry after that.
|
|
* Return NULL if this causes us to iterate past the beginning again.
|
|
*/
|
|
ktrace_entry_t *
|
|
ktrace_skip(
|
|
ktrace_t *ktp,
|
|
int count,
|
|
ktrace_snap_t *ktsp)
|
|
{
|
|
int index;
|
|
int new_index;
|
|
ktrace_entry_t *ktep;
|
|
int nentries = ktrace_nentries(ktp);
|
|
|
|
index = ktsp->ks_index;
|
|
new_index = index + count;
|
|
while (new_index >= nentries) {
|
|
new_index -= nentries;
|
|
}
|
|
if (index == ktsp->ks_start) {
|
|
/*
|
|
* We've iterated around to the start, so we're done.
|
|
*/
|
|
ktep = NULL;
|
|
} else if ((new_index < index) && (index < ktsp->ks_index)) {
|
|
/*
|
|
* We've skipped past the start again, so we're done.
|
|
*/
|
|
ktep = NULL;
|
|
ktsp->ks_index = ktsp->ks_start;
|
|
} else {
|
|
ktep = &(ktp->kt_entries[new_index]);
|
|
new_index++;
|
|
if (new_index == nentries) {
|
|
ktsp->ks_index = 0;
|
|
} else {
|
|
ktsp->ks_index = new_index;
|
|
}
|
|
}
|
|
return ktep;
|
|
}
|