kernel - Implement vm.read_shortcut support in tmpfs
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 21 Feb 2013 06:42:08 +0000 (22:42 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 21 Feb 2013 06:42:08 +0000 (22:42 -0800)
* Implement tmpfs support for vm.read_shorcut_enable=1

* Approximately doubles tmpfs read() performance on 64-bit systems
  for data sets which exceed the size of the buffer cache.

  Example using monster (64G ram, 6.4G buffer cache, 9G test data set)

    du -s -k /mp
9037196 /mp

    sysctl vm.read_shortcut_enable=1
    time tar cf /dev/nmonster# time tar cf /dev/null /mp
6.763u 13.275s 0:20.05 99.9%    26+66k 0+0io 0pf+0w
7.224u 12.830s 0:20.07 99.9%    26+66k 0+0io 0pf+0w
6.957u 14.924s 0:21.91 99.8%    26+66k 0+0io 0pf+0w

    sysctl vm.read_shortcut_enable=0
    time tar cf /dev/nmonster# time tar cf /dev/null /mp
7.510u 23.997s 0:31.52 99.9%    26+66k 0+0io 0pf+0w
7.769u 37.738s 0:45.53 99.9%    25+65k 0+0io 0pf+0w
7.716u 40.306s 0:48.04 99.9%    25+65k 0+0io 0pf+0w

* Note that variations in run time when the feature is disabled
  depends on what data is already present in the buffer cache and
  the cost of mapping new buffers and tearing down old buffers.
  This can be substantial on large multi-way systems due to
  SMP/page-table issues.

sys/vfs/tmpfs/tmpfs_vnops.c

index 4196c0e..c52ef65 100644 (file)
@@ -482,20 +482,36 @@ tmpfs_read (struct vop_read_args *ap)
        off_t base_offset;
        size_t offset;
        size_t len;
+       size_t resid;
        int error;
 
-       error = 0;
-       if (uio->uio_resid == 0) {
-               return error;
-       }
-
-       node = VP_TO_TMPFS_NODE(vp);
-
+       /*
+        * Check the basics
+        */
        if (uio->uio_offset < 0)
                return (EINVAL);
        if (vp->v_type != VREG)
                return (EINVAL);
 
+       /*
+        * Extract node, try to shortcut the operation through
+        * the VM page cache, allowing us to avoid buffer cache
+        * overheads.
+        */
+       node = VP_TO_TMPFS_NODE(vp);
+        resid = uio->uio_resid;
+        error = vop_helper_read_shortcut(ap);
+        if (error)
+                return error;
+        if (uio->uio_resid == 0) {
+               if (resid)
+                       goto finished;
+               return error;
+       }
+
+       /*
+        * Fall-through to our normal read code.
+        */
        while (uio->uio_resid > 0 && uio->uio_offset < node->tn_size) {
                /*
                 * Use buffer cache I/O (via tmpfs_strategy)
@@ -532,6 +548,7 @@ tmpfs_read (struct vop_read_args *ap)
                }
        }
 
+finished:
        TMPFS_NODE_LOCK(node);
        node->tn_status |= TMPFS_NODE_ACCESSED;
        TMPFS_NODE_UNLOCK(node);