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

Reply via email to