| /* | |
| * 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 | KM_LARGE); | |
| } | |
| 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; | |
| } |