HAMMER VFS - Fix deadlock which can occur under severe filesystem pressure
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 4 Feb 2011 19:55:02 +0000 (11:55 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 4 Feb 2011 19:55:02 +0000 (11:55 -0800)
* Inode reflushes (a fsync occuring while the inode is still queued for
  a prior fsync) were not ensuring that the inode got pushed to the backend
  flusher.

  This could lead to deadlocks when the process trying to issue the flush
  is the syncer itself.

* The problem typically occured under filesystem loads where a large number
  of inodes (aka due to a bulk build) are being flushed at once, and the
  flush is unable to finish running before the next syncer cycle comes
  around.

sys/vfs/hammer/hammer_inode.c

index 231883e..9448e16 100644 (file)
@@ -2370,6 +2370,21 @@ hammer_wait_inode(hammer_inode_t ip)
                       (ip->hmp->flags & HAMMER_MOUNT_CRITICAL_ERROR) == 0) {
                        if (ip->flush_state == HAMMER_FST_SETUP)
                                hammer_flush_inode(ip, HAMMER_FLUSH_SIGNAL);
+
+                       /*
+                        * If the inode was already being flushed its flg
+                        * may not have been queued to the backend.  We have
+                        * to make sure it gets queued or we can wind up
+                        * blocked or deadlocked (particularly if we are
+                        * the vnlru thread).
+                        */
+                       KKASSERT(ip->flush_group);
+                       if (ip->flush_group->closed == 0) {
+                               kprintf("hammer: debug: forcing async "
+                                       "flush ip %016jx\n",
+                                       (intmax_t)ip->obj_id);
+                               hammer_flusher_async(ip->hmp, ip->flush_group);
+                       }
                        if (ip->flush_state != HAMMER_FST_IDLE) {
                                ip->flags |= HAMMER_INODE_FLUSHW;
                                tsleep(&ip->flags, 0, "hmrwin", 0);