#include <sys/param.h>
#include <sys/systm.h>
#include <sys/eventhandler.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/msgbuf.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/mount.h>
#include <sys/tty.h>
#include <sys/tprintf.h>
#include <sys/syslog.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/conf.h>
#include <sys/sysproto.h>

#include <machine/pcb.h>
#include <machine/clock.h>
#include <machine/md_var.h>

#include <sys/utsname.h>
#include <sys/signalvar.h>

#include <machine/stdarg.h>

#include <sys/exec.h>
#include <sys/sysent.h>
#include <sys/errno.h>

void do_diskboot(void);

static int
diskboot_modevent(module_t mod, int type, void *data)
{
	int howto=1;
	struct buf *bp;
	int iter, nbusy, pbusy;

	switch (type) {
	case MOD_LOAD:
		break;
	default:
		return (EOPNOTSUPP);
	}

	printf("\nsyncing disks... ");

	sync(&proc0, NULL);

	for (iter = pbusy = 0; iter < 20; iter++) {
		nbusy = 0;
		for (bp = &buf[nbuf]; --bp >= buf; ) {
			if ((bp->b_flags & B_INVAL) == 0 &&
			    BUF_REFCNT(bp) > 0) {
				nbusy++;
			} else if ((bp->b_flags & (B_DELWRI | B_INVAL))
					== B_DELWRI) {
				/* bawrite(bp);*/
				nbusy++;
			}
		}
		if (nbusy == 0)
			break;
		printf("%d ", nbusy);
		if (nbusy < pbusy)
			iter = 0;
		pbusy = nbusy;
		sync(&proc0, NULL);
		DELAY(50000 * iter);
	}
	printf("\n");

	nbusy = 0;
	for (bp = &buf[nbuf]; --bp >= buf; ) {
		if (((bp->b_flags&B_INVAL) == 0 && BUF_REFCNT(bp)) ||
		    ((bp->b_flags & (B_DELWRI|B_INVAL)) == B_DELWRI)) {
			if (bp->b_dev == NODEV) {
				TAILQ_REMOVE(&mountlist,
				    bp->b_vp->v_mount, mnt_list);
				continue;
			}
			nbusy++;
		}
	}
	if (nbusy) {
		printf("giving up on %d buffers\n", nbusy);
		DELAY(5000000); /* 5 seconds */
	} else {
		printf("done\n");
		vfs_unmountall();
	}
	DELAY(100000);

	EVENTHANDLER_INVOKE(shutdown_post_sync, howto);
	splhigh();
	printf("Booting...\n");
	DELAY(1000000);
	
	do_diskboot();
	return 0;
}

DEV_MODULE(diskboot, diskboot_modevent, NULL);
