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

#include "def.h"

#define __KERNEL__
#include <xfs.h>
#include <xfs_log.h>
#include <xfs_trans.h>
#include <xfs_buf_item.h>
#include <xfs_inode_item.h>
#include <xfs_extfree_item.h>

#include "flags_trans.h"

void
prhead_xfs_log_item()
{
	mprintf(SPTR"        LSN "SPTR" "SPTR" "SPTR" "SPTR" "SPTR" TYPE     FLAGS\n",
		"ADDR", "DESC", "MOUNTP", "BIO_LIST", "CB", "OPS");
}

addr_t
print_xfs_log_item(addr, full)
	addr_t addr;
	int full;
{
	xfs_log_item_t li;
	static const struct bitname liflags[] = {
		{ XFS_LI_IN_AIL,	"in_ail" },
		{ XFS_LI_ABORTED,	"aborted" },
		{ 0,			NULL }
	};

	memread(addr, sizeof(li), &li, "xfs_log_item_t");
	mprintf(FPTR " ", addr);

	mprintf("%10llx ", (__s64)li.li_lsn);
	mprintf(FPTR " " FPTR, li.li_desc, li.li_mountp);
	mprintf(" " FPTR " " FPTR " " FPTR " ",
		li.li_bio_list, li.li_cb, li.li_ops);

	switch (li.li_type) {
	case XFS_LI_5_3_BUF:	mprintf("5.3BUF  ");	break;
	case XFS_LI_5_3_INODE:	mprintf("5.3INODE");	break;
	case XFS_LI_EFI:	mprintf("EFI     ");	break;
	case XFS_LI_EFD:	mprintf("EFD     ");	break;
	case XFS_LI_IUNLINK:	mprintf("IUNLINK ");	break;
	case XFS_LI_6_1_INODE:	mprintf("6.1INODE");	break;
	case XFS_LI_6_1_BUF:	mprintf("6.1BUF  ");	break;
	case XFS_LI_INODE:	mprintf("INODE   ");	break;
	case XFS_LI_BUF:	mprintf("BUF     ");	break;
	case XFS_LI_DQUOT:	mprintf("DQUOT   ");	break;
	case XFS_LI_QUOTAOFF:	mprintf("QUOTAOFF");	break;
#ifdef XFS_LI_RPC
	case XFS_LI_RPC:	mprintf("RPC     ");	break;
#endif
	}
	mprintbit(liflags, li.li_flags);
	mprintf("\n");

	if (full) {
		switch (li.li_type) {
		case XFS_LI_BUF: {
			xfs_buf_log_item_t bli;
			static const struct bitname bliflags[] = {
				{ XFS_BLI_HOLD,		"hold" },
				{ XFS_BLI_DIRTY,	"dirty" },
				{ XFS_BLI_STALE,	"stale" },
				{ XFS_BLI_LOGGED,	"logged" },
				{ XFS_BLI_INODE_ALLOC_BUF, "inode_alloc_buf" },
				{ 0,			NULL }
			};
			memread(addr, sizeof(bli), &bli, "xfs_buf_log_item");
			mprintf("  buf: " FPTR "\n", bli.bli_buf);
			mprintf("  flags:    %x ", bli.bli_flags);
			mprintbit(bliflags, bli.bli_flags);
			mprintf("\n");
			mprintf("  recur:    %x\n", bli.bli_recur);
			mprintf("  refcount: %x\n", ATOMIC_READ(bli.bli_refcount));
#if 0
			mprintf("  format.size:     %x\n", bli.bli_format.blf_size);
			mprintf("  format.flags:    %x\n", bli.bli_format.blf_flags);
			mprintf("  format.len:      %x\n", bli.bli_format.blf_len);
			mprintf("  format.blkno:    %llx\n", bli.bli_format.blf_blkno);
			mprintf("  format.map_size: %llx\n", bli.bli_format.blf_map_size);
#endif
			break;
		}
		case XFS_LI_INODE: {
			static const struct bitname iliflags[] = {
				{ XFS_ILI_HOLD,		   "hold" },
				{ XFS_ILI_IOLOCKED_EXCL,   "iolocked_excl" },
				{ XFS_ILI_IOLOCKED_SHARED, "iolocked_shared" },
				{ 0,			NULL }
			};
			xfs_inode_log_item_t ili;
			memread(addr, sizeof(ili), &ili, "xfs_inode_log_item");
			mprintf("  inode:       " FPTR "\n", ili.ili_inode);
			mprintf("  flags:       %x ", ili.ili_flags);
			mprintbit(iliflags, ili.ili_flags);
			mprintf("\n");
			mprintf("  extents_buf: " FPTR "\n", ili.ili_extents_buf);
			break;
		}
		case XFS_LI_EFI: {
			static const struct bitname efiflags[] = {
				{ XFS_EFI_RECOVERED,	"recovered" },
				{ XFS_EFI_COMMITTED,	"committed" },
				{ XFS_EFI_CANCELED,	"canceled" },
				{ 0,			NULL }
			};
			xfs_efi_log_item_t efi;
			memread(addr, sizeof(efi), &efi, "xfs_efi_log_item");
			mprintf("  flags:       %x ", efi.efi_flags);
			mprintbit(efiflags, efi.efi_flags);
			mprintf("\n");
			mprintf("  next_extent: %x\n", efi.efi_next_extent);
#if 0
			mprintf("  format.size: %x\n", efi.efi_format.efi_size);
#endif
			break;
		}
		case XFS_LI_EFD: {
			xfs_efd_log_item_t efd;
			memread(addr, sizeof(efd), &efd, "xfs_efd_log_item");
			mprintf("  efip: " FPTR "\n", efd.efd_efip);
			mprintf("  next_extent: %x\n", efd.efd_next_extent);
			break;
		}
		}
	}

	return (addr_t)li.li_ail.ail_forw;
}

const char *
xfs_transaction_type(t)
	int t;
{
	switch (t) {
	case XFS_TRANS_SETATTR_NOT_SIZE:	return "setattr_not_size";
	case XFS_TRANS_SETATTR_SIZE:		return "setattr_size";
	case XFS_TRANS_INACTIVE:		return "inactive";
	case XFS_TRANS_CREATE:			return "create";
	case XFS_TRANS_CREATE_TRUNC:		return "create_trunc";
	case XFS_TRANS_TRUNCATE_FILE:		return "truncate_file";
	case XFS_TRANS_REMOVE:			return "remove";
	case XFS_TRANS_LINK:			return "link";
	case XFS_TRANS_RENAME:			return "rename";
	case XFS_TRANS_MKDIR:			return "mkdir";
	case XFS_TRANS_RMDIR:			return "rmdir";
	case XFS_TRANS_SYMLINK:			return "symlink";
	case XFS_TRANS_SET_DMATTRS:		return "set_dmattrs";
	case XFS_TRANS_GROWFS:			return "growfs";
	case XFS_TRANS_STRAT_WRITE:		return "strat_write";
	case XFS_TRANS_DIOSTRAT:		return "diostrat";
	case XFS_TRANS_WRITE_SYNC:		return "write_sync";
	case XFS_TRANS_WRITEID:			return "writeio";
	case XFS_TRANS_ADDAFORK:		return "addafork";
	case XFS_TRANS_ATTRINVAL:		return "attrinval";
	case XFS_TRANS_ATRUNCATE:		return "atruncate";
	case XFS_TRANS_ATTR_SET:		return "attr_set";
	case XFS_TRANS_ATTR_RM:			return "attr_rm";
	case XFS_TRANS_CLEAR_AGI_BUCKET:	return "clear_agi_bucket";
	case XFS_TRANS_QM_SBCHANGE:		return "qm_sbchange";
	case XFS_TRANS_DUMMY1:			return "dummy1";
	case XFS_TRANS_DUMMY2:			return "dummy2";
	case XFS_TRANS_QM_QUOTAOFF:		return "qm_quotaoff";
	case XFS_TRANS_QM_DQALLOC:		return "qm_dqalloc";
	case XFS_TRANS_QM_SETQLIM:		return "qm_setqlim";
	case XFS_TRANS_QM_DQCLUSTER:		return "qm_dqcluster";
	case XFS_TRANS_QM_QINOCREATE:		return "qm_qinocreate";
	case XFS_TRANS_QM_QUOTAOFF_END:		return "qm_quotaoff_end";
	case XFS_TRANS_SB_UNIT:			return "sb_unit";
	case XFS_TRANS_FSYNC_TS:		return "fsync_ts";
	case XFS_TRANS_GROWFSRT_ALLOC:		return "growfsrt_alloc";	
	case XFS_TRANS_GROWFSRT_ZERO:		return "growfsrt_zero";
	case XFS_TRANS_GROWFSRT_FREE:		return "growfsrt_Free";
	case XFS_TRANS_SWAPEXT:			return "swapext";
	default:				return "<unknown>";
	}
}

void
prhead_xfs_trans()
{
	mprintf(SPTR " MAGIC   RES COUNT BLK_R             LSN ITEM     BUSY TYPE\n", "ADDR");
}

addr_t
print_xfs_trans(addr, aflag)
	addr_t addr;
	int aflag;
{
	struct xfs_trans trans;
	int i;
	static const struct bitname tflags[] = {
		{ XFS_TRANS_DIRTY,	"dirty" },
		{ XFS_TRANS_SB_DIRTY,	"sb_dirty" },
		{ XFS_TRANS_PERM_LOG_RES, "perm_log_res" },
		{ XFS_TRANS_SYNC,	"sync" },
		{ XFS_TRANS_DQ_DIRTY,	"dq_dirty" },
		{ XFS_TRANS_RESERVE,	"reserve" },
		{ 0,			NULL }
	};

	memread(addr, sizeof(trans), &trans, "xfs_trans_t");

	if (aflag) {
		mprintf(FPTR "  ", addr);
		for (i = 0; i < sizeof(trans.t_magic); i++) {
			int c = (trans.t_magic >> (sizeof(trans.t_magic) - i - 1) * 8) & 255;
			mprintf("%c", (c >= 0x20 && c < 0x7f)? c: '.');
		}
		mprintf(" %5x %5x %5x", trans.t_log_res, trans.t_log_count, trans.t_blk_res);
		mprintf(" %15llx", (__s64)trans.t_lsn);
		mprintf(" %4x %8x %s\n", 
			trans.t_items.lic_free, trans.t_busy.lbc_free,
			xfs_transaction_type(trans.t_type));
		goto out;
	}

	mprintf("addr:         " FPTR "\n", addr);
	mprintf("magic:        %x  ", trans.t_magic);
	for (i = 0; i < sizeof(trans.t_magic); i++) {
		int c = (trans.t_magic >> (sizeof(trans.t_magic) - i - 1) * 8) & 255;
		mprintf("%c", (c >= 0x20 && c < 0x7f)? c: '.');
	}
	mprintf("\n");

	mprintf("logcb.next:   " FPTR "\n", trans.t_logcb.cb_next);
	mprintf("logcb.func:   " FPTR " ( " FPTR " )\n",
					trans.t_logcb.cb_func,
					trans.t_logcb.cb_arg);
	mprintf("forw/back:    " FPTR " " FPTR "\n",
					trans.t_forw, trans.t_back);
	mprintf("type:         %x  %s\n", trans.t_type, xfs_transaction_type(trans.t_type));
	mprintf("log_res:      %x\n", trans.t_log_res);
	mprintf("log_count:    %x\n", trans.t_log_count);
	mprintf("blk_res:      %x\n", trans.t_blk_res);
	mprintf("blk_res_used: %x\n", trans.t_blk_res_used);
	mprintf("rtx_res:      %x\n", trans.t_rtx_res);
	mprintf("rtx_res_used: %x\n", trans.t_rtx_res_used);
	mprintf("ticket:       " FPTR "  (xlog_ticket)\n", trans.t_ticket);
	/* mprintf("sema:         \n");	XXX*/
	mprintf("lsn:          %llx\n", (__s64)trans.t_lsn);
#ifdef _xfs_trans_has_t_commit_lsn
	mprintf("commit_lsn:   %llx\n", (__s64)trans.t_commit_lsn);
#endif /*_xfs_trans_has_t_commit_lsn*/
	mprintf("mountp:       " FPTR "  (xfs_mount)\n", trans.t_mountp);
	mprintf("dqinfo:       " FPTR "\n", trans.t_dqinfo);
	mprintf("callback:     " FPTR "\n", trans.t_callback);
	mprintf("flags:        %x  ", trans.t_flags);
	mprintbit(tflags, trans.t_flags);
	mprintf("\n");
	mprintf("icount_delta:        %lx\n", trans.t_icount_delta);
	mprintf("ifree_delta:         %lx\n", trans.t_ifree_delta);
	mprintf("fdblocks_delta:      %lx\n", trans.t_fdblocks_delta);
	mprintf("res_fdblocks_delta:  %lx\n", trans.t_res_fdblocks_delta);
	mprintf("frextents_delta:     %lx\n", trans.t_frextents_delta);
	mprintf("res_frextents_delta: %lx\n", trans.t_res_frextents_delta);
	mprintf("ag_freeblks_delta    %lx\n", trans.t_ag_freeblks_delta);
	mprintf("ag_flist_delta:      %lx\n", trans.t_ag_flist_delta);
	mprintf("ag_btree_delta:      %lx\n", trans.t_ag_btree_delta);
	mprintf("dblocks_delta:       %lx\n", trans.t_dblocks_delta);
	mprintf("agcount_delta:       %lx\n", trans.t_agcount_delta);
	mprintf("imaxpct_delta:       %lx\n", trans.t_imaxpct_delta);
	mprintf("rextsize_delta:      %lx\n", trans.t_rextsize_delta);
	mprintf("rbmblocks_delta:     %lx\n", trans.t_rbmblocks_delta);
	mprintf("rblocks_delta:       %lx\n", trans.t_rblocks_delta);
	mprintf("rextents_delta:      %lx\n", trans.t_rextents_delta);
	mprintf("rextslog_delta:      %lx\n", trans.t_rextslog_delta);
	mprintf("items_free:          %x\n",  trans.t_items_free);
	mprintf("items.next:   " FPTR "\n",   trans.t_items.lic_next);
	mprintf("items.free/unused:   %x %x\n",trans.t_items.lic_free,
					      trans.t_items.lic_unused);
	for (i = 0; i < XFS_LIC_NUM_SLOTS; i++) {
		static const struct bitname lidflags[] = {
			{ XFS_LID_DIRTY,	"dirty" },
			{ XFS_LID_PINNED,	"pinned" },
			{ XFS_LID_SYNC_UNLOCK,	"sync_unlock" },
			{ 0,			NULL }
		};
		if (XFS_LIC_ISFREE(&trans.t_items, i))
			continue;
		mprintf("items.descs%2x: " FPTR "%4x %3x ", i,
			trans.t_items.lic_descs[i].lid_item,
			trans.t_items.lic_descs[i].lid_size,
			trans.t_items.lic_descs[i].lid_index);
		mprintbit(lidflags, trans.t_items.lic_descs[i].lid_flags);
		mprintf("\n");
	}
	mprintf("busy_free:           %x\n", trans.t_busy_free);
	mprintf("busy.next:    " FPTR "\n",   trans.t_busy.lbc_next);
	mprintf("busy.free/unused     %x %x\n", trans.t_busy.lbc_free,
						trans.t_busy.lbc_unused);
	for (i = 0; i < XFS_LBC_NUM_SLOTS; i++) {
		if (XFS_LBC_ISFREE(&trans.t_busy, i))
			continue;
		mprintf("items.busy %2x: %3x %3x\n", i,
			trans.t_busy.lbc_busy[i].lbc_ag,
			trans.t_busy.lbc_busy[i].lbc_idx);
	}

 out:
	if (trans.t_logcb.cb_next == 0)
		return 0;
	return (addr_t)trans.t_logcb.cb_next - OFFSET(struct xfs_trans, t_logcb.cb_next);
}
