/*
 * Copyright (C) 2000-2002 ASANO Masahiro
 */

#include "def.h"

#define __KERNEL__
#include <linux/spinlock.h>
#include <linux/slab.h>
#include "slab.h"

addr_t print_slab();
addr_t print_kmem_cache();

void
prhead_slab()
{
	mprintf(SPTR" "SPTR" COL "SPTR" INU FREE\n",
		"ADDR", "NEXT", "S_MEM");
}

addr_t
print_slab(addr, num)
	addr_t addr;
	int num;
{
	slab_t slab;
	kmem_bufctl_t buf[512], ix = slab.free;

	memread(addr, sizeof(slab), &slab, "slab_t");
	mprintf(FPTR " ", addr);

	mprintf(FPTR " %3lx " FPTR " %3x", slab.list.next, slab.colouroff, slab.s_mem, slab.inuse);

	if (slab.free == BUFCTL_END) {
		ix = slab.inuse;
		mprintf(" -\n");
	} else {
		if (num == 0 || num > LENGTHOF(buf)) {
			num = LENGTHOF(buf);
		}
		ix = slab.free;
		memread(addr + sizeof(slab), sizeof(buf[0]) * num, buf, "slab_t");
		while (1) {
			if (ix >= num) {
				mprintf(" ?\n");
				return 0;
			}
			mprintf(" %x", ix);
			if (buf[ix] == BUFCTL_END)
				break;
			ix = buf[ix];
		}
		mprintf("\n");
	}
	return (addr_t)slab.list.next - OFFSET(slab_t, list);
}

void
prhead_kmem_cache()
{
	mprintf(SPTR"  SIZE NUM  COL COFF NEXT  NAME\n", "ADDR");
}

addr_t
print_kmem_cache(addr, sflag, full)
	addr_t addr;
	int sflag;
	int full;
{
	kmem_cache_t cache;
	const char *p;

	memread(addr, sizeof(cache), &cache, "kmem_cache_t");

	if (!full) {
		mprintf(FPTR " %5x %3x %4lx %4x %4x  ",
			addr, cache.objsize, cache.num, cache.colour,
			cache.colour_off, cache.colour_next);
		mprint_str(cache.name, CACHE_NAMELEN);
		mprintf("\n");
	} else {
		mprintf("addr: " FPTR "\n", addr);
#if SUBLEVEL >= 9
		mprintf("slabs_full    " FPTR " " FPTR "\n",
			cache.slabs_full.next, cache.slabs_full.prev);
		mprintf("slabs_partial " FPTR " " FPTR "\n",
			cache.slabs_partial.next, cache.slabs_partial.prev);
		mprintf("slabs_free    " FPTR " " FPTR "\n",
			cache.slabs_free.next, cache.slabs_free.prev);
#else
		mprintf("slabs         " FPTR " " FPTR "\n",
			cache.slabs.next, cache.slabs.prev);
		mprintf("firstnotfull  " FPTR "\n", cache.firstnotfull);
#endif
		mprintf("objsize      %x\n", cache.objsize);
		mprintf("flags        %x\n", cache.flags);
		mprintf("num          %x\n", cache.num);
		mprintf("gfporder     %x\n", cache.gfporder);
		mprintf("gfpflags     %x\n", cache.gfpflags);
		mprintf("colour       %lx\n", cache.colour);
		mprintf("colour_off   %x\n", cache.colour_off);
		mprintf("colour_next  %x\n", cache.colour_next);
		mprintf("slabp_cache   " FPTR "\n", cache.slabp_cache);
		mprintf("growing      %x\n", cache.growing);
		mprintf("dflags       %x\n", cache.dflags);
		p = getsymstr((addr_t)cache.ctor);
		mprintf("ctor/dtor    %s", p? p: "NULL");
		p = getsymstr((addr_t)cache.dtor);
		mprintf("  %s\n", p? p: "NULL");
		mprintf("failures     %lx\n", cache.failures);
		mprintf("name         \"");
		mprint_str(cache.name, CACHE_NAMELEN);
		mprintf("\"\n");
		mprintf("next          " FPTR " " FPTR "\n",
			cache.next.next, cache.next.prev);
	}

	if (sflag) {
		addr_t sad;
#if SUBLEVEL >= 9
		int none;
		mprintf("slabs_full:\n");
		prhead_slab();
		sad = (addr_t)cache.slabs_full.next - OFFSET(slab_t, list);
		none = 1;
		while (sad && sad != addr + OFFSET(kmem_cache_t, slabs_full)) {
			sad = print_slab(sad, cache.num);
			none = 0;
		}
		if (none)
			mprintf(SPTR"\n", "(none)");

		mprintf("slabs_partial:\n");
		prhead_slab();
		sad = (addr_t)cache.slabs_partial.next - OFFSET(slab_t, list);
		none = 1;
		while (sad && sad != addr + OFFSET(kmem_cache_t, slabs_partial)) {
			sad = print_slab(sad, cache.num);
			none = 0;
		}
		if (none)
			mprintf(SPTR"\n", "(none)");
#else
		mprintf("slabs:\n");
		prhead_slab();
		sad = (addr_t)cache.slabs.next - OFFSET(slab_t, list);
		while (sad && sad != addr + OFFSET(kmem_cache_t, slabs))
			sad = print_slab(sad, cache.num);
#endif
	}
	return (addr_t)cache.next.next - OFFSET(kmem_cache_t, next);
}

int
search_slab(addr)
	addr_t addr;
{
	extern addr_t cache_cache_addr;
	static kmem_cache_t cache;
	addr_t kmaddr, saddr;
	slab_t slab;
	int len, n;

	kmaddr = cache_cache_addr;
	do {
		memread(kmaddr, sizeof(cache), &cache, "kmem_cache_t");
		len = cache.objsize * cache.num;

#if SUBLEVEL >= 9
		saddr = (addr_t)cache.slabs_full.next - OFFSET(slab_t, list);
		while (saddr && saddr != kmaddr + OFFSET(kmem_cache_t, slabs_full)) {
			memread(saddr, sizeof(slab), &slab, "slab_t");
			if (addr >= (addr_t)slab.s_mem && addr < (addr_t)slab.s_mem + len)
				goto found;
			saddr = (addr_t)slab.list.next - OFFSET(slab_t, list);
		}
		saddr = (addr_t)cache.slabs_partial.next - OFFSET(slab_t, list);
		while (saddr && saddr != kmaddr + OFFSET(kmem_cache_t, slabs_partial)) {
			memread(saddr, sizeof(slab), &slab, "slab_t");
			if (addr >= (addr_t)slab.s_mem && addr < (addr_t)slab.s_mem + len)
				goto found;
			saddr = (addr_t)slab.list.next - OFFSET(slab_t, list);
		}
#else
		saddr = (addr_t)cache.slabs.next - OFFSET(slab_t, list);
		while (saddr && saddr != kmaddr + OFFSET(kmem_cache_t, slabs)) {
			memread(saddr, sizeof(slab), &slab, "slab_t");
			if (addr >= (addr_t)slab.s_mem && addr < (addr_t)slab.s_mem + len)
				goto found;
			saddr = (addr_t)slab.list.next - OFFSET(slab_t, list);
		}
#endif
		kmaddr = (addr_t)cache.next.next - OFFSET(kmem_cache_t, next);
	} while (kmaddr && kmaddr != cache_cache_addr);

	return 0;	/* not found */
found:
	n = (addr - (addr_t)slab.s_mem) / cache.objsize;
	mprintf("slab \"%s\" offset %lx ", cache.name,
		(addr - (addr_t)slab.s_mem) - n * cache.objsize);

	if (slab.free == BUFCTL_END) {
		mprintf("inuse\n");
	} else {
		int num = cache.num;
		kmem_bufctl_t buf[512], ix = slab.free;

		if (num > LENGTHOF(buf))
			num = LENGTHOF(buf);
		ix = slab.free;
		memread(saddr + sizeof(slab), sizeof(buf[0]) * num, buf, "slab_t");
		while (1) {
			if (ix == n) {
				mprintf("free\n");
				break;
			} else if (ix < 0 || ix >= num) {
				mprintf("unknown\n");
				break;
			} else if (buf[ix] == BUFCTL_END) {
				mprintf("inuse\n");
				break;
			}
			ix = buf[ix];
		}
	}
	return 1;
}
