/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

#ident	"$Header: shm_vnodeops.c 1.15 90/01/31 $"

/*
 * Swap-based "file system" operations. Allocate backing store for the
 * "file" in swap space. This is not a full implementation - we know
 * that this is currently only used by the shared memory routines and
 * the data structures for this node are actually part of the shmid_ds
 * structure. Currently, only those VOPs required by mmap_reg are
 * implememented.
 *
 * To complete this, should break the link between this filesystem and
 * the shared memory operations. Allocate and manage a new type of
 * node (snode ? swapnode ?) and delete the vnode and dmap structures
 * from shmid_ds. Could perhaps use the current scheme (ie struct dmap)
 * to describe backing store. Or could go to a bitmap-based scheme
 * which gives us a finer granularity.
 */

/* $Log:	shm_vnodeops.c,v $
 */

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/sysmacros.h"
#include "sys/signal.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/systm.h"
#include "sys/mutex.h"
#include "sys/vm.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/buf.h"
#include "sys/vfs.h"
#include "sys/vnode.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/file.h"
#include "sys/uio.h"
#include "sys/conf.h"
#include "sys/cmap.h"
#include "sys/ufsinode.h"
#include "sys/ufsfilsys.h"
#include "sys/ufsmount.h"
#include "sys/ufsdir.h"
#include "sys/var.h"
#include "sys/open.h"
#include "sys/dirent.h"
#include "sys/cmn_err.h"
#include "sys/fcntl.h"
#include "sys/flock.h"
#include "sys/ipc.h"
#include "sys/shm.h"

#include "sys/pte.h"	/* For plocal */
#include "sys/plocal.h"	/* For l struct (rablock, etc) */
#include "sys/intctl.h"	/* For SPLs */

#include "sys/poll.h"
#include "sys/stropts.h"

/*ARGSUSED*/
static int
shm_nostrmsgioop(vp, mctl, mdata, rw, uflag, ioflag)
	struct vnode *vp;
	struct strbuf *mctl;
	struct strbuf *mdata;
	enum uio_rw rw;
	int uflag;
	int ioflag;
{
	return (ENOSTR);
}

/*ARGSUSED*/
static int
shm_nostrpollop(vp, events, anyyet)
	struct vnode *vp;
	short events;
	int anyyet;
{
	return (POLLNVAL);
}

static
shm_badop()
{
	cmn_err(CE_PANIC, "shm_badop");
	/*
	 *+ This filesystem type does not support the requested operation.
	 */
}

/*ARGSUSED*/
static
shm_getattr(vp, vap, cred)
	struct vnode *vp;
	register struct vattr *vap;
	struct ucred *cred;
{
	register struct shmid_ds *sp;
	extern int dmmin;

	sp = VTOM(vp);

	vap->va_type = VBLK;
	vap->va_mode = sp->shm_perm.mode;
	vap->va_uid = sp->shm_perm.uid;
	vap->va_gid = sp->shm_perm.gid;
	vap->va_fsid = swapdev;
	vap->va_nodeid = sp->shm_perm.seq; /* XXX */
	vap->va_nlink = sp->shm_refcnt;
	vap->va_flags = 0;
	vap->va_size = sp->shm_segsz;
	vap->va_atime.tv_sec = sp->shm_atime;
	vap->va_ctime.tv_sec = sp->shm_ctime;
	vap->va_mtime.tv_sec = sp->shm_dtime;
	vap->va_rdev = 0;
	vap->va_blocks = sp->shm_blocks;
	vap->va_blocksize = dmmin;
	return(0);
}

/*ARGSUSED*/
static
shm_setattr(vp, vap, cred)
	register struct vnode *vp;
	register struct vattr *vap;
	struct ucred *cred;
{
	register struct shmid_ds *sp;

	/*
	 * Can only set va_size or va_extfile. Disallow all else.
	 */
	if (vap->va_nlink != -1 || vap->va_blocksize != -1 ||
	    vap->va_rdev != -1 || vap->va_blocks != -1 ||
	    vap->va_fsid != -1 || vap->va_nodeid != -1 ||
	    (int)vap->va_mode != -1 || (int)vap->va_uid != -1 ||
	    (int)vap->va_gid != -1 || (int)vap->va_type != -1) {
		return(EINVAL);
	}

	sp = VTOM(vp);
	if (vap->va_size > sp->shm_segsz)
		cmn_err(CE_PANIC, "shm_setattr");
		/*
		 *+ The kernel attempted to grow the size of a shared
		 *+ memory segment.
		 */
	
	return(0);
}

/*
 * shm_bmap()
 *	- allocate allocate swap space and return disk block numbers
 *
 * Allocate swap space for this segment on the first call (shm_blocks
 * is 0). This implementation of bmap "knows" that only vinifod() calls
 * it. All calls with B_READ set in rwflg are failed - this ensures
 * that vinifod sets up a ZFOD page for this addr and also calls bmap
 * with B_WRITE to allocate a zeroed block.
 *
 * When we do a real swap-based filesystem, this should be fixed so that
 * the fs code maintains its own node and keeps track of which addresses
 * have swap blocks allocated.
 */
/*ARGSUSED*/
static
shm_bmap(vp, lbn, vpp, bnp, rwflg, size)
	struct vnode *vp;
	daddr_t lbn;
	struct vnode **vpp;
	daddr_t *bnp;
	int rwflg;
	int size;
{
	register int vsbase, i;
	register struct shmid_ds *sp = VTOM(vp);
	struct dmap *dmp = &sp->shm_dmap;
	struct dblock db;

	if (vpp)
		*vpp = vp->v_devvp;
	if (bnp == 0)
		return(0);

	if (sp->shm_blocks == 0) {
		/*
		 * Must clear the dmap entries here since we may
		 * be using a shmid_ds structure that was recently
		 * freed and there is code in vsalloc() which skips
		 * non-zero entries.
		 */
		for (i = 0; i < NDMAP; i++)
			dmp->dm_map[i] = (swblk_t)0;
		if (!vsalloc(dmp, (size_t)0, (size_t)btoc(sp->shm_segsz))) {
			u.u_error = EAGAIN;
			*bnp = -1;
			return(0);
		}
		/*
		 * ensure adjacent blocks in the dmap structure are not
		 * contiguous. "bug" in swap driver - see comments in
		 * fix_dmap().
		 */
		if (!fix_dmap(dmp, btoc(sp->shm_segsz))) {
			vsrele(dmp);
			u.u_error = EAGAIN;
			*bnp = -1;
			return(0);
		}
		sp->shm_blocks = ctod(btoc(sp->shm_segsz));
	}

	if (rwflg & B_READ) {
		*bnp = -1;
		return(0);
	}

	vsbase = ctod(lbn * vp->v_vfsp->vfs_bsize / CLBYTES);
	vstodb(vsbase, ctod(1), dmp, &db, 0);
	if (db.db_base == 0) {
		/* alloc failed */
		u.u_error = EAGAIN;
		*bnp = -1;
		return(0);
	}

	*bnp = db.db_base;
	return(0);
}

/*ARGSUSED*/
static
shm_fsync(vp, cred)
	struct vnode *vp;
	struct ucred *cred;
{
}

static
shm_inactive(vp, flag)
	register struct vnode *vp;
	int flag;
{
	if (flag == NODEISLOCKED) {
		VN_UNLOCKNODE(vp);
	}
	vsrele(&(VTOM(vp)->shm_dmap));
	VN_UNLOCK(vp);
}

struct vnodeops shm_vnodeops = {
	shm_badop,	 /* vn_open     */
	shm_badop,	 /* vn_close    */
	shm_badop,	 /* vn_rdwr     */
	shm_badop,	 /* vn_ioctl    */
	shm_getattr,	 /* vn_geattr   */
	shm_setattr,	 /* vn_setattr  */
	shm_badop,	 /* vn_access   */
	shm_badop,	 /* vn_lookup   */
	shm_badop,	 /* vn_creat    */
	shm_badop,	 /* vn_remove   */
	shm_badop,	 /* vn_link     */
	shm_badop,	 /* vn_rename   */
	shm_badop,       /* vn_mkdir    */
	shm_badop,	 /* vn_rmdir    */
	shm_badop,	 /* vn_readdir  */
	shm_badop,       /* vn_symlink  */
	shm_badop,	 /* vn_readlink */
	shm_fsync,	 /* vn_fsync    */
	shm_inactive,	 /* vn_inactive */
	shm_bmap,	 /* vn_bmap     */
	shm_badop,	 /* vn_strategy */
	shm_badop,	 /* vn_bread    */
	shm_badop,	 /* vn_brelse   */
	shm_badop,	 /* vn_minphys  */
	shm_badop,	 /* vn_getdents */
	shm_badop,	 /* vn_fdclose  */
	shm_badop,	 /* vn_fcntl    */
	shm_badop,	 /* vn_realvp   */
	shm_badop,	 /* vn_vhangup  */
	shm_nostrmsgioop,/* vn_strmsgio */
	shm_nostrpollop	 /* vn_strpoll  */
};
