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

#include "def.h"

#define __KERNEL__
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/nfs3.h>
#include <linux/nfs_fs_i.h>
#include <linux/nfs_fs_sb.h>
#include <linux/nfs_flushd.h>
#include <linux/nfs_page.h>

#include "flags_nfs_fs_i.h"
#include "flags_nfs_fs_sb.h"

void
mprint_nfs_fh(buf)
	const void *buf;
{
	const struct nfs_fh *fhp = buf;
	int i;

	mprintf("%2x:", fhp->size);
	for (i = 0; i < NFS_MAXFHSIZE && i < fhp->size; i++) {
		if (i == 32)
			mprintf("\n  : ");
		else if (i % 4 == 0)
			mprintf(" ");
		mprintf("%02x", fhp->data[i]);
	}
	mprintf("\n");
}

void
_prhead_nfs_page(str)
	const char *str;
{
	mprintf(SPTR" "SPTR" "SPTR" "SPTR" "SPTR" TIMEO OFFSET BYTES COUNT\n",
		str, "FILE", "INODE", "CRED", "PAGE");
}

void
prhead_nfs_page()
{
	_prhead_nfs_page("ADDR");
}

addr_t
print_nfs_page(addr, full)
	addr_t addr;
	int full;
{
	struct nfs_page page;

	memread(addr, sizeof(page), &page, "nfs_page");
	mprintf(FPTR " ", addr);
	mprintf(FPTR " " FPTR " " FPTR " " FPTR " ",
		page.wb_file, page.wb_inode, page.wb_cred, page.wb_page);
	mprintf("%5lx %6x %5x %5x",
		page.wb_timeout, page.wb_offset, page.wb_bytes, page.wb_count);
	mprintf("\n");
	return (addr_t)page.wb_list.next;
}

void
prhead_nfs_inode()
{
	mprintf(SPTR"   FILEID RD DT CM PG FLAGS\n", "ADDR");
}

addr_t
print_nfs_inode(addr, full)
	addr_t addr;
	int full;
{
#ifdef _nfs_inode_info_has_fileid
	struct nfs_inode_info ni;
	static const struct bitname nflags[] = {
		{ NFS_INO_STALE,	"ino_stale" },
		{ NFS_INO_ADVISE_RDPLUS,"ino_advise_rdplus" },
		{ NFS_INO_REVALIDATING,	"ino_revalidating" },
		{ NFS_IS_SNAPSHOT,	"is_snapshot" },
		{ NFS_INO_FLUSH,	"ino_flush" },
		{ 0,			NULL }
	};

	memread(addr, sizeof(ni), &ni, "nfs_inode_info");
	mprintf(FPTR " ", addr);
	mprintf("%8llx", ni.fileid);
	mprintf(" %2x %2x %2x %2x",
		ni.nread, ni.ndirty, ni.ncommit, ni.npages);
	mprintbit(nflags, ni.flags);
	mprintf("\n");

	if (full) {
		mprint_nfs_fh(&ni.fh);
		mprintf("read_cache_jiffies  %lx\n", ni.read_cache_jiffies);
		mprintf("read_cache_ctime    %llx\n", ni.read_cache_ctime);
		mprintf("read_cache_mtime    %llx\n", ni.read_cache_mtime);
		mprintf("read_cache_isize    %llx\n", ni.read_cache_isize);
		mprintf("attrtimeo           %lx\n", ni.attrtimeo);
		mprintf("attrtimeo_timestamp %lx\n", ni.attrtimeo_timestamp);
		mprintf("cookieverf          %x  %x\n",
					ni.cookieverf[0], ni.cookieverf[1]);
#ifdef _nfs_inode_info_has_mm_cred
		mprintf("mm_cred             " FPTR "\n", ni.mm_cred);
#endif /*_nfs_inode_info_has_mm_cred*/
	}

	return (addr_t)ni.read.next - OFFSET(struct nfs_inode_info, read);
#else /*_nfs_inode_info_has_fileid*/
	return 0;
#endif /*_nfs_inode_info_has_fileid*/
}

void
_prhead_nfs_reqlist(str)
	const char *str;
{
	mprintf(SPTR " NR    RUNAT " SPTR " " SPTR " " SPTR "\n",
		str, "TASK", "AUTH", "INODES");
}

void
prhead_nfs_reqlist()
{
	_prhead_nfs_reqlist("ADDR");
}

addr_t
print_nfs_reqlist(addr, full)
	addr_t addr;
	int full;
{
	struct nfs_reqlist rl;

	memread(addr, sizeof(rl), &rl, "nfs_reqlist");
	mprintf(FPTR " ", addr);
	mprintf("%2x %8lx ", ATOMIC_READ(rl.nr_requests), rl.runat);
	mprintf(FPTR " " FPTR " " FPTR "\n", rl.task, rl.auth, rl.inodes);
	return 0;
}

void
prhead_nfs_server()
{
	mprintf(SPTR " " SPTR " " SPTR " RSIZE WSIZE DTSIZ BSIZE " SPTR " RDCB FLG NAME\n",
		"ADDR", "CLIENT", "RPC_OPS", "RW_REQ");
}

addr_t
print_nfs_server(dummy, addr, full)
	addr_t dummy;
	addr_t addr;
	int full;
{
	struct nfs_server server;
	char buf[64];

	memread(addr, sizeof(server), &server, "nfs_server");
	mprintf(FPTR " ", addr);
	mprintf(FPTR " " FPTR " ", server.client, server.rpc_ops);
	mprintf("%5x %5x %5x %5x ",
		server.rsize, server.wsize,
		server.dtsize, server.bsize);
	mprintf(FPTR " ", server.rw_requests);

#ifdef _nfs_server_has_lru_busy
#define IS_LISTED(list) ((addr_t)server.list.next != addr + OFFSET(struct nfs_server,list))
	mprintf("%c%c%c%c ",
		IS_LISTED(lru_read)?   'r': '-',
		IS_LISTED(lru_dirty)?  'd': '-',
		IS_LISTED(lru_commit)? 'c': '-',
		IS_LISTED(lru_busy)?   'b': '-');
#endif /*_nfs_server_has_lru_read*/

	mprintf("%3x ", server.flags);
	memread((addr_t)server.hostname, sizeof(buf), buf, "nfs_server.hostname");
	mprintstr(buf, sizeof(buf));
	mprintf("\n");

	if (full) {
		addr_t tmp;

		mprintf("    REG %d-%d   DIR %d-%d\n",
			server.acregmin, server.acregmax,
			server.acdirmin, server.acdirmax);
		if (server.rw_requests) {
			_prhead_nfs_reqlist("REQLIST");
			print_nfs_reqlist((addr_t)server.rw_requests, 0);
		}
#ifdef _nfs_server_has_lru_busy
		_prhead_nfs_page("READ");
		tmp = (addr_t)server.lru_read.next;
		while (tmp && tmp != addr + OFFSET(struct nfs_server, lru_read)) {
			tmp = print_nfs_page(tmp - OFFSET(struct nfs_page, wb_lru));
		}

		_prhead_nfs_page("DIRTY");
		tmp = (addr_t)server.lru_dirty.next;
		while (tmp && tmp != addr + OFFSET(struct nfs_server, lru_dirty)) {
			tmp = print_nfs_page(tmp - OFFSET(struct nfs_page, wb_lru));
		}

		_prhead_nfs_page("COMMIT");
		tmp = (addr_t)server.lru_commit.next;
		while (tmp && tmp != addr + OFFSET(struct nfs_server, lru_commit)) {
			tmp = print_nfs_page(tmp - OFFSET(struct nfs_page, wb_lru));
		}

		_prhead_nfs_page("BUSY");
		tmp = (addr_t)server.lru_busy.next;
		while (tmp && tmp != addr + OFFSET(struct nfs_server, lru_busy)) {
			tmp = print_nfs_page(tmp - OFFSET(struct nfs_page, wb_lru));
		}
#endif
		mprintf("\n");
	}

	return 0;
}
