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
[email protected]
https://lists.sourceforge.net/lists/listinfo/open-vm-tools-devel