I've been working on an experimental patch for mmap() support. It's experimental since I haven't done rigorous tests on it yet, only some basic checks. But, at least, cp works now! Taking a look at truss on a before/after:
stat("file",{ mode=-rwx------ ,inode=2884760722,size=5,blksize=4096 }) = 0 (0x0) stat("file2",{ mode=-rwx------ ,inode=749578205,size=5,blksize=4096 }) = 0 (0x0) open("file",O_RDONLY,00) = 2 (0x2) open("file2",O_WRONLY|O_TRUNC,00) = 3 (0x3) mmap(0x0,5,PROT_READ,MAP_SHARED,2,0x0) ERR#22 'Invalid argument' [1] read(2,"asdf\n",1048576) = 5 (0x5) [2] - defaults to read() write(3,"asdf\n",5) = 5 (0x5) read(2,0x800a1e000,1048576) = 0 (0x0) close(3) = 0 (0x0) close(2) = 0 (0x0) [1] - This is where mmap failed before even without a VOP_GETPAGES/VOP_PUTPAGES implementation. It occurrs in vm_mmap_vnode(). We have to create a vnode object if we want to mmap, and so we do so in HgfsOpenFile(). Secondly, after a VOP_GETPAGES/VOP_PUTPAGES is implemented, MAP_SHARED would still fail with EPERM! The reason is simple but it took me a while to realize it! Again we fail in vm_mmap_vnode() but at: if ((flags & MAP_SHARED) != 0) { if ((va.va_flags & (SF_SNAPSHOT|IMMUTABLE|APPEND)) != 0) { if (prot & PROT_WRITE) { error = EPERM; goto done; I must admit I didn't understand why such flags would be set in the first place, so after a bit of digging I came to HgfsAttrToBSD() in fsutil.c: /* * Initialize all fields to zero. We don't need to do this for Mac OS * because the VATTR_RETURN macros take care of it for us. */ #if defined __FreeBSD__ VATTR_NULL(vap); #endif Interestingly enough, VATTR_NULL doesn't quite zero everything: vattr_null(struct vattr *vap) { vap->va_type = VNON; vap->va_size = VNOVAL; ... vap->va_flags = VNOVAL; ... } In fact, most fields are set to VNOVAL which is defined in vnode.h as (-1)!. So, our va_flags field is set to 0xffffffff and so vm_mmap_vnode()'s check succeeds and gives us EPERM. I've set va_flags to 0 in HgfsAttrToBSD() since it isn't used anywhere else in vmhgfs code. That being solved, we may use cp! Here's the after shot: stat("file",{ mode=-rwx------ ,inode=2884760722,size=5,blksize=4096 }) = 0 (0x0) stat("file2",{ mode=-rwx------ ,inode=749578205,size=5,blksize=4096 }) = 0 (0x0) open("file",O_RDONLY,00) = 2 (0x2) open("file2",O_WRONLY|O_TRUNC,00) = 3 (0x3) mmap(0x0,5,PROT_READ,MAP_SHARED,2,0x0) = 34365227008 (0x80053c000) <- works happy! write(3,"asdf\n",5) = 5 (0x5) munmap(0x80053c000,5) = 0 (0x0) close(3) = 0 (0x0) close(2) = 0 (0x0) I've also commented out the call to flush and setSize in HgfsRemoveInt() since I don't see it being used as such in any filesystems I've checked for reference. SMBFS, NWFS don't make a call to vm_object_page_clean() (which leads to VOP_PUTPAGES() eventually). NFSCLIENT however does make a call to invalidate its buffers but from what I see, it only flushes the buffer cache when a VOP_REMOVE is called upon so I've left it out. It also panics the system when I leave it in :-D So, I'll do a lot more tests with mmap but I post it here in case anyone else would also like to do some tests and figure out any bugs so we can solve them quicker that way. A few final thoughts on GETPAGES/PUTPAGES however: Since FreeBSD-CURRENT (900012) some differences were made with the locking of the page queues. In the version I'm testing this on (RELENG_8_1), all systems are still using vm_page_lock_queues() ie smbfs/nwfs/nfsclient/etc in their implementations. Since 900012, this is no longer being done. Individual pages are locked with vm_page_lock which is new. I haven't a clue how vmhgfs will work in this case on current. SMBFS/NWFS/NFSCLIENT/etc have been updated but some systems are still using the older method. I can't find any reference to the change in cvs log so I'm not sure. I'll have to test it out and if it's being a bother, some ugly macros would have to be placed in. One final problem, none of this would compile on xnu. I do not have osx so I unfortunately cannot test this :( But from what I've looked at (very briefly) at how xnu does it, it's quite different so none of this would compile. Actually I don't quite understand how it compiles anyway even without the mmap patch since I do not have osx to try it! Has anybody tried to run hgfs on an apple recently? Moving away from mmap, there appear to be two final big problems to solve. The EIO problem as referred to in HgfsCreateInt() and a vnode referencing issue. The latter one is a really big problem. If one were to stick around in a directory under /mnt/hgfs/somedir and do a forceful unmount and a subsequent 'ls' a panic would occurr. I believe it may be related with the fact that some vnodes stay referenced even after an unmount + kldunload with a reference count of two. Particularly for instance "somedir" in the above example will stay with a ref of 2. This happens every time but I havn't had a chance to do a proper test since I've been busy with the mmap thing. I'll check that out later and after that I can finally begin what I intended to do in the first place and port open-vm-tools to NetBSD! Here's the patch that works against the previous ones. Yes I know, all these patches... I've got a bit of a patch managment issue! diff -uNr open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/fsutil.c open-vm-tools/modules/freebsd/vmhgfs/fsutil.c --- open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/fsutil.c 2010-09-20 18:30:55.000000000 +0000 +++ open-vm-tools/modules/freebsd/vmhgfs/fsutil.c 2010-10-18 02:40:39.000000000 +0000 @@ -669,6 +669,8 @@ HGFS_VATTR_NLINK_RETURN(vap, 1); /* fake */ + vap->va_flags = 0; + if (sip->uidSet || (hgfsAttrV2->mask & HGFS_ATTR_VALID_USERID) == 0) { HGFS_VATTR_UID_RETURN(vap, sip->uid); } else { diff -uNr open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/hgfs_kernel.h open-vm-tools/modules/freebsd/vmhgfs/hgfs_kernel.h --- open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/hgfs_kernel.h 2010-09-20 18:30:55.000000000 +0000 +++ open-vm-tools/modules/freebsd/vmhgfs/hgfs_kernel.h 2010-10-18 02:42:15.000000000 +0000 @@ -151,6 +151,8 @@ * Global variables */ +extern int hgfs_pbuf_freecnt; + /* * The vnode attributes between Mac OS and FreeBSD are very similar but not exactly the * same. Fields have names have changed. However, only HgfsAttrToBSD and diff -uNr open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/os.c open-vm-tools/modules/freebsd/vmhgfs/os.c --- open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/os.c 2010-09-20 18:30:55.000000000 +0000 +++ open-vm-tools/modules/freebsd/vmhgfs/os.c 2010-10-18 02:40:39.000000000 +0000 @@ -32,10 +32,13 @@ #include <sys/malloc.h> #include <sys/lock.h> // for struct mtx #include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/buf.h> // for nswbuf #include <vm/uma.h> // for uma_zone_t #include <sys/kthread.h> // for kthread_create() #include <vm/vm.h> #include <vm/vm_extern.h> // for vnode_pager_setsize +#include <vm/vm_object.h> #include "vm_basic_types.h" #include "os.h" @@ -55,6 +58,8 @@ struct uma_zone *umaZone; } os_zone_struct; +int hgfs_pbuf_freecnt = -1; + /* *---------------------------------------------------------------------------- * @@ -76,7 +81,8 @@ int os_init(void) { - /* NOP */ + /* Initialize physical buffer free counter */ + hgfs_pbuf_freecnt = (nswbuf / 2); return 0; } @@ -897,7 +903,7 @@ * Flushes dirty pages associated with the file. * * Results: - * Always retun 0 (success) for now since it is NOOP. + * Cleans pages associated with vnode object. * * Side effects: * None. @@ -909,11 +915,8 @@ off_t start, // IN: starting offset in the file to flush uint32_t length) // IN: length of data to flush { - /* - * XXX: NOOP for now. This routine is needed to maintain coherence - * between memory mapped data and data for read/write operations. - * Will need to implement when adding support for memory mapped files to HGFS - * for FreeBsd. - */ + VM_OBJECT_LOCK(vp->v_object); + vm_object_page_clean(vp->v_object, 0, 0, OBJPC_SYNC); + VM_OBJECT_UNLOCK(vp->v_object); return 0; } diff -uNr open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/vnops.c open-vm-tools/modules/freebsd/vmhgfs/vnops.c --- open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/vnops.c 2010-10-18 02:44:28.000000000 +0000 +++ open-vm-tools/modules/freebsd/vmhgfs/vnops.c 2010-10-18 02:40:39.000000000 +0000 @@ -23,8 +23,10 @@ */ #include <sys/param.h> // for everything +#include <sys/systm.h> // for KASSERT #include <sys/vnode.h> // for struct vnode #include <sys/mount.h> // for struct mount +#include <sys/buf.h> // for struct buf #include <sys/namei.h> // for name lookup goodness #include <sys/libkern.h> // for string & other functions #include <sys/fcntl.h> // for in-kernel file access flags (FREAD, etc) @@ -32,6 +34,12 @@ #include <sys/uio.h> // for uiomove #include <sys/dirent.h> // for struct dirent +#include <vm/vm.h> +#include <vm/vm_page.h> +#include <vm/vm_extern.h> +#include <vm/vm_object.h> +#include <vm/vm_pager.h> + #include "cpName.h" #include "hgfsUtil.h" @@ -68,6 +76,8 @@ static vop_print_t HgfsVopPrint; static vop_readlink_t HgfsVopReadlink; static vop_symlink_t HgfsVopSymlink; +static vop_getpages_t HgfsVopGetPages; +static vop_putpages_t HgfsVopPutPages; /* * Global data @@ -97,6 +107,8 @@ .vop_print = HgfsVopPrint, .vop_readlink = HgfsVopReadlink, .vop_symlink = HgfsVopSymlink, + .vop_getpages = HgfsVopGetPages, + .vop_putpages = HgfsVopPutPages, }; @@ -863,3 +875,42 @@ return HgfsSymlinkInt(dvp, vp, cnp, target); } +int +HgfsVopGetPages(struct vop_getpages_args *ap) +/* +struct vop_getpages_args { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_reqpage; + vm_ooffset_t a_offset; +}; +*/ +{ + struct vnode *vp = ap->a_vp; + vm_page_t *pages = ap->a_m; + int count = ap->a_count; + int reqpage = ap->a_reqpage; + + return HgfsGetPagesInt(vp, pages, count, reqpage); +} + +int HgfsVopPutPages(struct vop_putpages_args *ap) +/* +struct vop_getpages_args { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_sync; + int *a_rtvals; + vm_ooffset_t a_offset; +} +*/ +{ + struct vnode *vp = ap->a_vp; + struct vm_page **pages = ap->a_m; + int count = ap->a_count; + int *rtvals = ap->a_rtvals; + + return HgfsPutPagesInt(vp, pages, count, rtvals); +} diff -uNr open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/vnopscommon.c open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c --- open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/vnopscommon.c 2010-09-20 18:30:55.000000000 +0000 +++ open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c 2010-10-18 03:00:30.000000000 +0000 @@ -23,8 +23,16 @@ */ #include <sys/param.h> // for everything +#include <sys/systm.h> #include <sys/vnode.h> // for struct vnode #include <sys/dirent.h> // for struct dirent +#include <sys/buf.h> +#include <vm/vm.h> +#include <vm/vm_page.h> +#include <vm/vm_extern.h> +#include <vm/vm_object.h> +#include <vm/vm_pager.h> + #include "fsutil.h" #include "debug.h" @@ -694,12 +702,14 @@ goto out; } - os_FlushRange(vp, 0, HGFS_VP_TO_FILESIZE(vp)); - os_SetSize(vp, 0); +// os_FlushRange(vp, 0, HGFS_VP_TO_FILESIZE(vp)); +// os_SetSize(vp, 0); /* We can now send the delete request. */ ret = HgfsDelete(sip, HGFS_VP_TO_FILENAME(vp), HGFS_OP_DELETE_FILE_V3); + cache_purge(vp); + out: DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp)); return ret; @@ -1712,6 +1722,9 @@ } } + /* Create backing object for this vnode. */ + vnode_create_vobject(vp, fp->fileSize, curthread); + os_rw_lock_unlock_exclusive(fp->handleLock); out: @@ -3056,3 +3069,215 @@ } return error; } + +/* + *---------------------------------------------------------------------------- + * + * HgfsGetPagesInt -- + * + * HgfsGetPagesInt is invoked to read in pages from the backing store. + * Generally one page need only be loaded but VOP_GETPAGES is requested to + * read in adjacent pages as well, although it is not required to do so. + * + * Results: + * VM_PAGER_OK on success or VM_PAGER_ERROR otherwise. + * + * Side effects: + * + *---------------------------------------------------------------------------- + */ + +int HgfsGetPagesInt(struct vnode *vp, + vm_page_t *pages, + int count, + int reqpage) +{ + int npages, size, toff, nextoff, i, error; + struct uio uio; + struct iovec iov; + struct buf *bp; + vm_page_t m; + vm_object_t object; + vm_offset_t kva; + + npages = btoc(count); + + printf("************* HgfsVopGetPages ***************\n"); + printf("count: %d\n", count); + printf("npages: %d\n", npages); + + m = pages[reqpage]; + + if ((object = vp->v_object) == NULL) { + return VM_PAGER_ERROR; + } + + VM_OBJECT_LOCK(object); + if (m->valid != 0) { + vm_page_lock_queues(); + for (i = 0; i < npages; ++i) { + if (i != reqpage) { + vm_page_free(pages[i]); + } + vm_page_unlock_queues(); + VM_OBJECT_UNLOCK(object); + return VM_PAGER_OK; + } + } + VM_OBJECT_UNLOCK(object); + + /* Obtain a buffer and a temporary mapping */ + bp = getpbuf(&hgfs_pbuf_freecnt); + kva = (vm_offset_t) bp->b_data; + pmap_qenter(kva, pages, npages); + + iov.iov_base = (caddr_t) kva; + iov.iov_len = count; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); + uio.uio_resid = count; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_td = curthread; + + error = HgfsReadInt(vp, &uio, TRUE); + + /* Remove temporary mappings and release physical buffer */ + pmap_qremove(kva, npages); + relpbuf(bp, &hgfs_pbuf_freecnt); + + VM_OBJECT_LOCK(object); + if (error && (uio.uio_resid == count)) { + printf("HgfsVopGetPages error: %d\n", error); + vm_page_lock_queues(); + for (i = 0; i < npages; i++) { + if (reqpage != i) { + vm_page_free(pages[i]); + } + vm_page_unlock_queues(); + VM_OBJECT_UNLOCK(object); + return VM_PAGER_ERROR; + } + } + + size = count - uio.uio_resid; + + vm_page_lock_queues(); + for (i = 0, toff = 0; i < npages; i++, toff = nextoff) { + struct vm_page *mt; + + mt = pages[i]; + nextoff = toff + PAGE_SIZE; + if (nextoff <= size) { + /* Read filled an entire page */ + mt->valid = VM_PAGE_BITS_ALL; + KASSERT(mt->dirty == 0, ("HgfsGetPages: page %p is dirty\n", mt)); + } + else if (size > toff) { + /* Read filled a partial page */ + mt->valid = 0; + vm_page_set_valid(mt, 0, size - toff); + KASSERT(mt->dirty == 0, ("HgfsGetPages: page %p is dirty\n", mt)); + } + else { + /* + * Read operation was short. If no error occurred, leave m->valid + * as zero and the pager will call vm_page_zero_invalid() to zero + * fill the non-valid portions of the page. This often denots that + * an end of file has been mapped into memory and the file's size + * is not page aligned (vm_pager.c:vm_page_zero_invalid()). + */ + ; + } + + if (i != reqpage) { + if (!error) { + if (mt->oflags & VPO_WANTED) + vm_page_activate(mt); + else + vm_page_deactivate(mt); + vm_page_wakeup(mt); + } + else { + vm_page_free(mt); + } + } + } + vm_page_unlock_queues(); + VM_OBJECT_UNLOCK(object); + if (error) { + printf("HgfsGetPages: Read I/O error: %d\n", error); + } + return (error ? VM_PAGER_ERROR : VM_PAGER_OK); +} + +/* + *---------------------------------------------------------------------------- + * + * HgfsPutPagesInt -- + * + * HgfsPutPagesInt is invoked to write dirty pages to the backing store. + * + * Results: + * VM_PAGER_OK on success or VM_PAGER_ERROR otherwise. + * + * Side effects: + * + *---------------------------------------------------------------------------- + */ + +int +HgfsPutPagesInt(struct vnode *vp, + vm_page_t *pages, + int count, + int *rtvals) +{ + int i, npages, error; + struct uio uio; + struct iovec iov; + struct buf *bp; + vm_offset_t kva; + + npages = btoc(count); + + printf("********** HgfsVopPutPages *************\n"); + printf("count: %d\n", count); + + for (i = 0; i < npages; i++) { + rtvals[i] = VM_PAGER_AGAIN; + } + + /* Obtain a buffer and a temporary mapping */ + bp = getpbuf(&hgfs_pbuf_freecnt); + kva = (vm_offset_t) bp->b_data; + pmap_qenter(kva, pages, npages); + + iov.iov_base = (caddr_t) kva; + iov.iov_len = count; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); + uio.uio_resid = count; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_td = curthread; + + error = HgfsWriteInt(vp, &uio, 0, TRUE); + + /* Remove temporary mappings and release physical buffer */ + pmap_qremove(kva, npages); + relpbuf(bp, &hgfs_pbuf_freecnt); + + /* Tell the pager how many pages were written and undirty the pages */ + if (!error) { + int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; + vm_page_lock_queues(); + for (i = 0; i < nwritten; i++) { + rtvals[i] = VM_PAGER_OK; + vm_page_undirty(pages[i]); + } + vm_page_unlock_queues(); + } + return rtvals[0]; +} diff -uNr open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/vnopscommon.h open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.h --- open-vm-tools-2010.09.19-301124/modules/freebsd/vmhgfs/vnopscommon.h 2010-09-20 18:30:55.000000000 +0000 +++ open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.h 2010-10-18 02:42:15.000000000 +0000 @@ -90,5 +90,8 @@ char *targetName); int HgfsMmapInt(struct vnode *vp, int accessMode); int HgfsMnomapInt(struct vnode *vp); +int HgfsGetPagesInt(struct vnode *vp, vm_page_t *pages, int count, int reqpage); +int HgfsPutPagesInt(struct vnode *vp, vm_page_t *pages, int count, int *rtvals); + #endif // _HGFS_VNOPS_COMMON_H_ ------------------------------------------------------------------------------ Download new Adobe(R) Flash(R) Builder(TM) 4 The new Adobe(R) Flex(R) 4 and Flash(R) Builder(TM) 4 (formerly Flex(R) Builder(TM)) enable the development of rich applications that run across multiple browsers and platforms. Download your free trials today! http://p.sf.net/sfu/adobe-dev2dev _______________________________________________ open-vm-tools-devel mailing list open-vm-tools-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/open-vm-tools-devel