hammer2 - multi-thread read-ahead XOPs
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 9 Jun 2016 05:24:51 +0000 (22:24 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 9 Jun 2016 06:03:52 +0000 (23:03 -0700)
* Distribute asynchronous logical buffer read-ahead XOPs to multiple
  worker threads.  XOPs related to a particular inode are usually sent
  to just one worker thread to reduce collision retries.

  This works around the high messaging overhead(s) associated with the
  current XOP architecture by spreading the pain around.  And even though
  the default check code is now xxhash, distributing the checks also helps
  a great deal.  The H2 chain topology actually parallelizes quite well for
  read operations.

  Streaming reads through the filesystem now run at over 1 GByte/sec (they
  capped out at ~340MB/sec before).  The effect on things like 'tar'
  are not quite as pronounced but small-file scan/read performance will
  typically improve by a tiny bit too.

* This change is probably more SSD-friendly than HDD-friendly for streaming
  reads due to out-of-order queueing of the I/O requests.  I ran a quick
  read test on a WD black and it appeared to perform acceptably so for
  now I'm going to run with it.  Adjusting read-ahead scale via
  vfs.hammer2.cluster_enable can be used to find a good value (for now).

* Remove the 'get race' kprintfs.  This case now occurs very often due
  to distributed read-aheads.

* chain->flags must use atomic ops, fix some cases I muffed up in recent
  commits.

sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_chain.c
sys/vfs/hammer2/hammer2_strategy.c
sys/vfs/hammer2/hammer2_thread.c

index 5491eb6..e4bea67 100644 (file)
@@ -864,6 +864,7 @@ struct hammer2_xop_head {
        uint32_t                check_counter;
        uint32_t                run_mask;
        uint32_t                chk_mask;
+       int                     flags;
        int                     state;
        int                     error;
        hammer2_key_t           collect_key;
@@ -1028,6 +1029,7 @@ typedef struct hammer2_xop_group hammer2_xop_group_t;
  * MODIFYING   - This is a modifying transaction, allocate a mtid.
  */
 #define HAMMER2_XOP_MODIFYING          0x00000001
+#define HAMMER2_XOP_ITERATOR           0x00000002
 
 /*
  * Global (per partition) management structure, represents a hard block
index d1984dd..e861753 100644 (file)
@@ -1721,6 +1721,9 @@ hammer2_chain_get(hammer2_chain_t *parent, int generation,
         * access the RBTREE, and it is possible to collide with another
         * hammer2_chain_get() operation because the caller might only hold
         * a shared lock on the parent.
+        *
+        * NOTE: Get races can occur quite often when we distribute
+        *       asynchronous read-aheads across multiple threads.
         */
        KKASSERT(parent->refs > 0);
        error = hammer2_chain_insert(parent, chain,
@@ -1729,7 +1732,7 @@ hammer2_chain_get(hammer2_chain_t *parent, int generation,
                                     generation);
        if (error) {
                KKASSERT((chain->flags & HAMMER2_CHAIN_ONRBTREE) == 0);
-               kprintf("chain %p get race\n", chain);
+               /*kprintf("chain %p get race\n", chain);*/
                hammer2_chain_drop(chain);
                chain = NULL;
        } else {
@@ -2049,8 +2052,10 @@ again:
                chain = hammer2_chain_get(parent, generation,
                                          &bcopy);
                if (chain == NULL) {
+                       /*
                        kprintf("retry lookup parent %p keys %016jx:%016jx\n",
                                parent, key_beg, key_end);
+                       */
                        goto again;
                }
                if (bcmp(&bcopy, bref, sizeof(bcopy))) {
index e382055..39cf650 100644 (file)
@@ -271,7 +271,11 @@ hammer2_strategy_read(struct vop_strategy_args *ap)
        lbase = bio->bio_offset;
        KKASSERT(((int)lbase & HAMMER2_PBUFMASK) == 0);
 
-       xop = hammer2_xop_alloc(ip, 0);
+       if (bp->b_bio1.bio_flags & BIO_SYNC) {
+               xop = hammer2_xop_alloc(ip, 0);
+       } else {
+               xop = hammer2_xop_alloc(ip, HAMMER2_XOP_ITERATOR);
+       }
        xop->finished = 0;
        xop->bio = bio;
        xop->lbase = lbase;
index 4a1859f..34aead2 100644 (file)
@@ -250,6 +250,7 @@ hammer2_xop_alloc(hammer2_inode_t *ip, int flags)
 
        xop->head.ip1 = ip;
        xop->head.func = NULL;
+       xop->head.flags = flags;
        xop->head.state = 0;
        xop->head.error = 0;
        xop->head.collect_key = 0;
@@ -375,9 +376,13 @@ hammer2_xop_start_except(hammer2_xop_head_t *xop, hammer2_xop_func_t func,
        if (pmp->has_xop_threads == 0)
                hammer2_xop_helper_create(pmp);
 
-       /*ng = pmp->xop_iterator++;*/
-       ng = (int)(hammer2_icrc32(&xop->ip1, sizeof(xop->ip1)) ^
-                  hammer2_icrc32(&func, sizeof(func)));
+       if (xop->flags & HAMMER2_XOP_ITERATOR) {
+               ng = (int)(hammer2_icrc32(&xop->ip1, sizeof(xop->ip1)) ^
+                          pmp->xop_iterator++);
+       } else {
+               ng = (int)(hammer2_icrc32(&xop->ip1, sizeof(xop->ip1)) ^
+                          hammer2_icrc32(&func, sizeof(func)));
+       }
        ng = ng & HAMMER2_XOPGROUPS_MASK;
 #if 0
        g = pmp->xop_iterator++;