--- old/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h	2012-07-20 12:37:50.889623702 +0800
+++ new/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h	2012-07-20 12:37:50.582519460 +0800
@@ -253,6 +253,8 @@
 void smbfs_addfree(struct smbnode *sp);
 void smbfs_rmhash(struct smbnode *);
 
+void smbfs_invalidate_pages(vnode_t *vp, u_offset_t off, cred_t *cr);
+
 /* See avl_create in smbfs_vfsops.c */
 void smbfs_init_hash_avl(avl_tree_t *);
 
--- old/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c	2012-07-20 12:37:51.543707033 +0800
+++ new/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c	2012-07-20 12:37:51.236938389 +0800
@@ -157,9 +157,10 @@
 	cred_t		*oldcr;
 	char 		*orpath;
 	int		orplen;
+	vnode_t		*vp;
 
 	/*
-	 * Flush and invalidate all pages (todo)
+	 * Flush and invalidate all pages
 	 * Free any held credentials and caches...
 	 * etc.  (See NFS code)
 	 */
@@ -179,6 +180,11 @@
 
 	mutex_exit(&np->r_statelock);
 
+	vp = SMBTOV(np);
+	if (vn_has_cached_data(vp)) {
+		smbfs_invalidate_pages(vp, (u_offset_t) 0, oldcr);
+	}
+
 	if (ovsa.vsa_aclentp != NULL)
 		kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz);
 
@@ -1040,9 +1046,45 @@
  */
 /*ARGSUSED*/
 void
-smbfs_rflush(struct vfs *vfsp, cred_t *cr)
-{
-	/* Todo: mmap support. */
+smbfs_rflush(struct vfs *vfsp, cred_t *cr) {
+
+    smbmntinfo_t *mi;
+    smbnode_t *np;
+    vnode_t *vp;
+
+    long num, cnt;
+
+    vnode_t **vplist;
+
+    mi = VFTOSMI(vfsp);
+
+    cnt = 0;
+    num = mi->smi_hash_avl.avl_numnodes;
+    vplist = kmem_alloc(num * sizeof (vnode_t*), KM_SLEEP);
+
+    rw_enter(&mi->smi_hash_lk, RW_READER);
+    for (np = avl_first(&mi->smi_hash_avl); np != NULL;
+            np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
+        vp = SMBTOV(np);
+        if (vn_is_readonly(vp))
+            continue;
+
+        if (vn_has_cached_data(vp) && (np->r_flags & RDIRTY || np->r_mapcnt > 0)) {
+            VN_HOLD(vp);
+            vplist[cnt++] = vp;
+            if (cnt == num)
+                break;
+        }
+    }
+    rw_exit(&mi->smi_hash_lk);
+
+    while (cnt-- > 0) {
+        vp = vplist[cnt];
+        (void) VOP_PUTPAGE(vp, 0, 0, 0, cr, NULL);
+        VN_RELE(vp);
+    }
+
+    kmem_free(vplist, num * sizeof (vnode_t*));
 }
 
 /* access cache */
--- old/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c	2012-07-20 12:37:52.205226128 +0800
+++ new/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c	2012-07-20 12:37:51.892117700 +0800
@@ -51,6 +51,18 @@
 #include <sys/vfs_opreg.h>
 #include <sys/policy.h>
 
+#include <sys/param.h>
+#include <sys/vm.h>
+#include <vm/seg_vn.h>
+#include <vm/pvn.h>
+#include <vm/as.h>
+#include <vm/hat.h>
+#include <vm/page.h>
+#include <vm/seg.h>
+#include <vm/seg_map.h>
+#include <vm/seg_kmem.h>
+#include <vm/seg_kpm.h>
+
 #include <netsmb/smb_osdep.h>
 #include <netsmb/smb.h>
 #include <netsmb/smb_conn.h>
@@ -175,6 +187,38 @@
 static int	smbfs_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *,
 			caller_context_t *);
 
+static int uio_page_mapin(uio_t *uiop, page_t *pp);
+
+static void uio_page_mapout(uio_t *uiop, page_t *pp);
+
+static int smbfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
+        size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+        caller_context_t *ct);
+
+static int smbfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+        size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+        caller_context_t *ct);
+
+static int smbfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+        size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr,
+        caller_context_t *ct);
+
+static int smbfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags,
+        cred_t *cr, caller_context_t *ct);
+
+static int smbfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, size_t *lenp,
+        int flags, cred_t *cr);
+
+static int smbfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
+        page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+        enum seg_rw rw, cred_t *cr, caller_context_t *ct);
+
+static int smbfs_getapage(vnode_t *vp, u_offset_t off, size_t len,
+        uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+        enum seg_rw rw, cred_t *cr);
+
+
+
 /* Dummy function to use until correct function is ported in */
 int noop_vnodeop() {
 	return (0);
@@ -215,11 +259,12 @@
 	{ VOPNAME_FRLOCK,	{ .vop_frlock = smbfs_frlock } },
 	{ VOPNAME_SPACE,	{ .vop_space = smbfs_space } },
 	{ VOPNAME_REALVP,	{ .error = fs_nosys } }, /* smbfs_realvp, */
-	{ VOPNAME_GETPAGE,	{ .error = fs_nosys } }, /* smbfs_getpage, */
-	{ VOPNAME_PUTPAGE,	{ .error = fs_nosys } }, /* smbfs_putpage, */
-	{ VOPNAME_MAP,		{ .error = fs_nosys } }, /* smbfs_map, */
-	{ VOPNAME_ADDMAP,	{ .error = fs_nosys } }, /* smbfs_addmap, */
-	{ VOPNAME_DELMAP,	{ .error = fs_nosys } }, /* smbfs_delmap, */
+	{ VOPNAME_GETPAGE,	{ .vop_getpage = smbfs_getpage } }, /* smbfs_getpage, */
+	{ VOPNAME_PUTPAGE,	{ .vop_putpage = smbfs_putpage } }, /* smbfs_putpage, */
+	{ VOPNAME_MAP,		{ .vop_map = smbfs_map } }, /* smbfs_map, */
+	{ VOPNAME_ADDMAP,	{ .vop_addmap = smbfs_addmap } }, /* smbfs_addmap, */
+	{ VOPNAME_DELMAP,	{ .vop_delmap = smbfs_delmap } }, /* smbfs_delmap, */
+	{ VOPNAME_DISPOSE,	{ .vop_dispose = fs_dispose}},
 	{ VOPNAME_DUMP,		{ .error = fs_nosys } }, /* smbfs_dump, */
 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = smbfs_pathconf } },
 	{ VOPNAME_PAGEIO,	{ .error = fs_nosys } }, /* smbfs_pageio, */
@@ -486,7 +531,35 @@
 	(void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0);
 	smb_credinit(&scred, cr);
 
-	smbfs_rele_fid(np, &scred);
+	/*
+	 * If FID ref. count is 1 and count of mmaped pages isn't 0,
+	 * we won't call smbfs_rele_fid(), because it will result in the otW close.
+	 * The count of mapped pages isn't 0, which means the mapped pages
+	 * possibly will be accessed after close(), we should keep the FID valid,
+	 * i.e., dont do the otW close.
+	 * Dont worry that FID will be leaked, because when the
+	 * vnode's count becomes 0, smbfs_inactive() will
+	 * help us release FID and eventually do the otW close.
+	 */
+	if (np->n_fidrefs > 1) {
+		smbfs_rele_fid(np, &scred);
+	} else if (np->r_mapcnt == 0) {
+		/*
+		 * Before otW close, make sure dirty pages written back.
+		 */
+		if ((flag & FWRITE) && vn_has_cached_data(vp)) {
+			/* smbfs_putapage() will acquire shared lock, so release
+		 	 * exclusive lock temporally.
+		 	 */
+			smbfs_rw_exit(&np->r_lkserlock);
+
+			(void) smbfs_putpage(vp, (offset_t) 0, 0, B_INVAL | B_ASYNC, cr, ct);
+
+			/* acquire exclusive lock again. */
+			(void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0);
+		}
+		smbfs_rele_fid(np, &scred);
+	}
 
 	smb_credrele(&scred);
 	smbfs_rw_exit(&np->r_lkserlock);
@@ -1365,6 +1438,20 @@
 			break;
 		SMBVDEBUG("open file: refs %d id 0x%x path %s\n",
 		    np->n_fidrefs, np->n_fid, np->n_rpath);
+		/*
+		 * Before otW close, make sure dirty pages written back.
+		 */
+		if (vn_has_cached_data(vp)) {
+			/* smbfs_putapage() will acquire shared lock, so release
+			 * exclusive lock temporally.
+			 */
+			smbfs_rw_exit(&np->r_lkserlock);
+
+			(void) smbfs_putpage(vp, (offset_t) 0, 0, B_INVAL | B_ASYNC, cr, ct);
+
+			/* acquire exclusive lock again. */
+			(void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0);
+		}
 		/* Force last close. */
 		np->n_fidrefs = 1;
 		smbfs_rele_fid(np, &scred);
@@ -3110,3 +3197,472 @@
 	else
 		return (ENOSYS);
 }
+
+static int uio_page_mapin(uio_t *uiop, page_t *pp) {
+    u_offset_t off;
+    size_t size;
+    pgcnt_t npages;
+    caddr_t kaddr;
+    pfn_t pfnum;
+
+    off = (uintptr_t) uiop->uio_loffset & PAGEOFFSET;
+    size = P2ROUNDUP(uiop->uio_resid + off, PAGESIZE);
+    npages = btop(size);
+
+    ASSERT(pp != NULL);
+
+    if (npages == 1 && kpm_enable) {
+        kaddr = hat_kpm_mapin(pp, NULL);
+        if (kaddr == NULL)
+            return (EFAULT);
+
+        uiop->uio_iov->iov_base = kaddr + off;
+        uiop->uio_iov->iov_len = PAGESIZE - off;
+
+    } else {
+        kaddr = vmem_xalloc(heap_arena, size, PAGESIZE, 0, 0, NULL, NULL, VM_SLEEP);
+        if (kaddr == NULL)
+            return (EFAULT);
+
+        uiop->uio_iov->iov_base = kaddr + off;
+        uiop->uio_iov->iov_len = size - off;
+
+        /*map pages into kaddr*/
+        uint_t attr = PROT_READ | PROT_WRITE | HAT_NOSYNC;
+        while (npages-- > 0) {
+            pfnum = pp->p_pagenum;
+            pp = pp->p_next;
+
+            hat_devload(kas.a_hat, kaddr, PAGESIZE, pfnum, attr, HAT_LOAD_LOCK);
+            kaddr += PAGESIZE;
+        }
+    }
+    return (0);
+}
+
+static void uio_page_mapout(uio_t *uiop, page_t *pp) {
+    u_offset_t off;
+    size_t size;
+    pgcnt_t npages;
+    caddr_t kaddr;
+
+    kaddr = uiop->uio_iov->iov_base;
+    off = (uintptr_t) kaddr & PAGEOFFSET;
+    size = P2ROUNDUP(uiop->uio_iov->iov_len + off, PAGESIZE);
+    npages = btop(size);
+
+    ASSERT(pp != NULL);
+
+    kaddr = (caddr_t) ((uintptr_t) kaddr & MMU_PAGEMASK);
+
+    if (npages == 1 && kpm_enable) {
+        hat_kpm_mapout(pp, NULL, kaddr);
+
+    } else {
+        hat_unload(kas.a_hat, (void*) kaddr, size,
+                HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK);
+        vmem_free(heap_arena, (void*) kaddr, size);
+    }
+    uiop->uio_iov->iov_base = 0;
+    uiop->uio_iov->iov_len = 0;
+}
+
+static int smbfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
+        size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+        caller_context_t *ct) {
+    smbnode_t *np;
+    smbmntinfo_t *smi;
+    struct vattr va;
+    segvn_crargs_t vn_a;
+    int error;
+
+    np = VTOSMB(vp);
+    smi = VTOSMI(vp);
+
+    if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
+        return (EIO);
+
+    if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+        return (EIO);
+
+    if (vp->v_flag & VNOMAP || vp->v_flag & VNOCACHE)
+        return (EAGAIN);
+
+    if (vp->v_type != VREG)
+        return (ENODEV);
+
+    va.va_mask = AT_ALL;
+    if (error = smbfsgetattr(vp, &va, cr))
+        return (error);
+
+    if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, SMBINTR(vp)))
+        return (EINTR);
+
+    if (MANDLOCK(vp, va.va_mode)) {
+        error = EAGAIN;
+        goto out;
+    }
+
+    as_rangelock(as);
+    error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags);
+
+    if (error != 0) {
+        as_rangeunlock(as);
+        goto out;
+    }
+
+    vn_a.vp = vp;
+    vn_a.offset = off;
+    vn_a.type = flags & MAP_TYPE;
+    vn_a.prot = prot;
+    vn_a.maxprot = maxprot;
+    vn_a.flags = flags & ~MAP_TYPE;
+    vn_a.cred = cr;
+    vn_a.amp = NULL;
+    vn_a.szc = 0;
+    vn_a.lgrp_mem_policy_flags = 0;
+
+    error = as_map(as, *addrp, len, segvn_create, &vn_a);
+
+    as_rangeunlock(as);
+
+out:
+    smbfs_rw_exit(&np->r_lkserlock);
+
+    return (error);
+}
+
+static int smbfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+        size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+        caller_context_t *ct) {
+    atomic_add_long((ulong_t *) & VTOSMB(vp)->r_mapcnt, btopr(len));
+    return (0);
+}
+
+static int smbfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+        size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr,
+        caller_context_t *ct) {
+
+    smbnode_t *np;
+
+    atomic_add_long((ulong_t *) & VTOSMB(vp)->r_mapcnt, -btopr(len));
+
+    /* mark RDIRTY here, will be used to check if a file is dirty when unmount smbfs */
+    if (vn_has_cached_data(vp) && !vn_is_readonly(vp) && maxprot & PROT_WRITE && flags == MAP_SHARED) {
+        np = VTOSMB(vp);
+        mutex_enter(&np->r_statelock);
+        np->r_flags |= RDIRTY;
+        mutex_exit(&np->r_statelock);
+    }
+    return (0);
+}
+
+static int smbfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags,
+        cred_t *cr, caller_context_t *ct) {
+
+    smbnode_t *np;
+    size_t io_len;
+    u_offset_t io_off;
+    u_offset_t eoff;
+    int error = 0;
+    page_t *pp;
+
+    np = VTOSMB(vp);
+
+    if (len == 0) {
+        /* will flush the file, so clear RDIRTY */
+        if (off == (u_offset_t) 0 && (np->r_flags & RDIRTY)) {
+            mutex_enter(&np->r_statelock);
+            np->r_flags &= ~RDIRTY;
+            mutex_exit(&np->r_statelock);
+        }
+
+        error = pvn_vplist_dirty(vp, off, smbfs_putapage, flags, cr);
+    } else {
+
+        eoff = off + len;
+
+        mutex_enter(&np->r_statelock);
+        if (eoff > np->r_size)
+            eoff = np->r_size;
+        mutex_exit(&np->r_statelock);
+
+        for (io_off = off; io_off < eoff; io_off += io_len) {
+            if ((flags & B_INVAL) || (flags & B_ASYNC) == 0) {
+                pp = page_lookup(vp, io_off,
+                        (flags & (B_INVAL | B_FREE) ? SE_EXCL : SE_SHARED));
+            } else {
+                pp = page_lookup_nowait(vp, io_off,
+                        (flags & B_FREE) ? SE_EXCL : SE_SHARED);
+            }
+
+            if (pp == NULL || !pvn_getdirty(pp, flags))
+                io_len = PAGESIZE;
+            else {
+                error = smbfs_putapage(vp, pp, &io_off, &io_len, flags, cr);
+            }
+        }
+
+    }
+
+    return (error);
+}
+
+static int smbfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, size_t *lenp,
+        int flags, cred_t *cr) {
+
+    struct smb_cred scred;
+    smbnode_t *np;
+    smbmntinfo_t *smi;
+    smb_share_t *ssp;
+    uio_t uio;
+    iovec_t uiov, uiov_bak;
+
+    size_t io_len;
+    u_offset_t io_off;
+    size_t bsize;
+    size_t blksize;
+    u_offset_t blkoff;
+    int error;
+
+    np = VTOSMB(vp);
+    smi = VTOSMI(vp);
+    ssp = smi->smi_share;
+
+    /*do block io, get a kluster of dirty pages in a block.*/
+    bsize = MAX(vp->v_vfsp->vfs_bsize, PAGESIZE);
+    blkoff = pp->p_offset / bsize;
+    blkoff *= bsize;
+    blksize = roundup(bsize, PAGESIZE);
+
+    pp = pvn_write_kluster(vp, pp, &io_off, &io_len, blkoff, blksize, flags);
+
+    ASSERT(pp->p_offset >= blkoff);
+
+    if (io_off + io_len > blkoff + blksize) {
+        ASSERT((io_off + io_len)-(blkoff + blksize) < PAGESIZE);
+        io_len = blkoff + blksize - io_off;
+    }
+
+    /*currently, don't allow put pages beyond EOF, unless smbfs_read/smbfs_write
+     *can do io through segkpm or vpm.*/
+    mutex_enter(&np->r_statelock);
+    if (io_off >= np->r_size) {
+        mutex_exit(&np->r_statelock);
+        error = 0;
+        goto out;
+    } else if (io_off + io_len > np->r_size) {
+        int npages = btopr(np->r_size - io_off);
+        page_t *trunc;
+        page_list_break(&pp, &trunc, npages);
+        if (trunc)
+            pvn_write_done(trunc, flags);
+        io_len = np->r_size - io_off;
+    }
+    mutex_exit(&np->r_statelock);
+
+    if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
+        return (EINTR);
+    smb_credinit(&scred, cr);
+
+    if (np->n_vcgenid != ssp->ss_vcgenid)
+        error = ESTALE;
+    else {
+        /*just use uio instead of buf, since smb_rwuio need uio.*/
+        uiov.iov_base = 0;
+        uiov.iov_len = 0;
+        uio.uio_iov = &uiov;
+        uio.uio_iovcnt = 1;
+        uio.uio_loffset = io_off;
+        uio.uio_resid = io_len;
+        uio.uio_segflg = UIO_SYSSPACE;
+        uio.uio_llimit = MAXOFFSET_T;
+        /*map pages into kernel address space, and setup uio.*/
+        error = uio_page_mapin(&uio, pp);
+        if (error == 0) {
+            uiov_bak.iov_base = uiov.iov_base;
+            uiov_bak.iov_len = uiov.iov_len;
+            error = smb_rwuio(ssp, np->n_fid, UIO_WRITE, &uio, &scred, smb_timo_write);
+            if (error == 0) {
+                mutex_enter(&np->r_statelock);
+                np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
+                mutex_exit(&np->r_statelock);
+                (void) smbfs_smb_flush(np, &scred);
+            }
+            /*unmap pages from kernel address space.*/
+            uio.uio_iov = &uiov_bak;
+            uio_page_mapout(&uio, pp);
+        }
+    }
+
+    smb_credrele(&scred);
+    smbfs_rw_exit(&np->r_lkserlock);
+
+out:
+    pvn_write_done(pp, ((error) ? B_ERROR : 0) | B_WRITE | flags);
+
+    if (offp)
+        *offp = io_off;
+    if (lenp)
+        *lenp = io_len;
+
+    return (error);
+}
+
+static int smbfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
+        page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+        enum seg_rw rw, cred_t *cr, caller_context_t *ct) {
+
+    int error;
+
+    /*these pages have all protections.*/
+    if (protp)
+        *protp = PROT_ALL;
+
+    if (len <= PAGESIZE) {
+        error = smbfs_getapage(vp, off, len, protp, pl, plsz, seg, addr, rw,
+                cr);
+    } else {
+        error = pvn_getpages(smbfs_getapage, vp, off, len, protp, pl, plsz, seg,
+                addr, rw, cr);
+    }
+
+    return (error);
+}
+
+static int smbfs_getapage(vnode_t *vp, u_offset_t off, size_t len,
+        uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+        enum seg_rw rw, cred_t *cr) {
+
+    smbnode_t *np;
+    smbmntinfo_t *smi;
+    smb_share_t *ssp;
+    smb_cred_t scred;
+
+    page_t *pp;
+    uio_t uio;
+    iovec_t uiov, uiov_bak;
+
+    u_offset_t blkoff;
+    size_t bsize;
+    size_t blksize;
+
+    u_offset_t io_off;
+    size_t io_len;
+    size_t pages_len;
+
+    int error = 0;
+
+    np = VTOSMB(vp);
+    smi = VTOSMI(vp);
+    ssp = smi->smi_share;
+
+    /*if pl is null,it's meaningless*/
+    if (pl == NULL)
+        return (EFAULT);
+
+again:
+    if (page_exists(vp, off) == NULL) {
+        if (rw == S_CREATE) {
+            /*just return a empty page if asked to create.*/
+            if ((pp = page_create_va(vp, off, PAGESIZE, PG_WAIT | PG_EXCL, seg, addr)) == NULL)
+                goto again;
+            pages_len = PAGESIZE;
+        } else {
+
+            /*do block io, get a kluster of non-exist pages in a block.*/
+            bsize = MAX(vp->v_vfsp->vfs_bsize, PAGESIZE);
+            blkoff = off / bsize;
+            blkoff *= bsize;
+            blksize = roundup(bsize, PAGESIZE);
+
+            pp = pvn_read_kluster(vp, off, seg, addr, &io_off, &io_len, blkoff, blksize, 0);
+
+            if (pp == NULL)
+                goto again;
+
+            pages_len = io_len;
+
+            /*currently, don't allow get pages beyond EOF, unless smbfs_read/smbfs_write
+             *can do io through segkpm or vpm.*/
+            mutex_enter(&np->r_statelock);
+            if (io_off >= np->r_size) {
+                mutex_exit(&np->r_statelock);
+                error = 0;
+                goto out;
+            } else if (io_off + io_len > np->r_size) {
+                int npages = btopr(np->r_size - io_off);
+                page_t *trunc;
+
+                page_list_break(&pp, &trunc, npages);
+                if (trunc)
+                    pvn_read_done(trunc, 0);
+                io_len = np->r_size - io_off;
+            }
+            mutex_exit(&np->r_statelock);
+
+            if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
+                return EINTR;
+            smb_credinit(&scred, cr);
+
+            /*just use uio instead of buf, since smb_rwuio need uio.*/
+            uiov.iov_base = 0;
+            uiov.iov_len = 0;
+            uio.uio_iov = &uiov;
+            uio.uio_iovcnt = 1;
+            uio.uio_loffset = io_off;
+            uio.uio_resid = io_len;
+            uio.uio_segflg = UIO_SYSSPACE;
+            uio.uio_llimit = MAXOFFSET_T;
+
+            /*map pages into kernel address space, and setup uio.*/
+            error = uio_page_mapin(&uio, pp);
+            if (error == 0) {
+                uiov_bak.iov_base = uiov.iov_base;
+                uiov_bak.iov_len = uiov.iov_len;
+                error = smb_rwuio(ssp, np->n_fid, UIO_READ, &uio, &scred, smb_timo_read);
+                /*unmap pages from kernel address space.*/
+                uio.uio_iov = &uiov_bak;
+                uio_page_mapout(&uio, pp);
+            }
+
+            smb_credrele(&scred);
+            smbfs_rw_exit(&np->r_lkserlock);
+        }
+    } else {
+        se_t se = rw == S_CREATE ? SE_EXCL : SE_SHARED;
+        if ((pp = page_lookup(vp, off, se)) == NULL) {
+            goto again;
+        }
+    }
+
+out:
+    if (pp) {
+        if (error) {
+            pvn_read_done(pp, B_ERROR);
+        } else {
+            /*init page list, unlock pages.*/
+            pvn_plist_init(pp, pl, plsz, off, pages_len, rw);
+        }
+    }
+
+    return (error);
+}
+
+
+void smbfs_invalidate_pages(vnode_t *vp, u_offset_t off, cred_t *cr) {
+
+    smbnode_t *np;
+
+    np = VTOSMB(vp);
+    /* will flush the file, so clear RDIRTY */
+    if (off == (u_offset_t) 0 && (np->r_flags & RDIRTY)) {
+        mutex_enter(&np->r_statelock);
+        np->r_flags &= ~RDIRTY;
+        mutex_exit(&np->r_statelock);
+    }
+
+    (void) pvn_vplist_dirty(vp, off, smbfs_putapage, B_INVAL | B_TRUNC, cr);
+}
+
+
