kernel - Fix rare buffer cache deadlock
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 12 Dec 2013 17:01:40 +0000 (09:01 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 12 Dec 2013 17:05:25 +0000 (09:05 -0800)
* cluster_collectbufs() was improperly using a blocking vfs/bio calls
  to find nearby buffers, which can deadlock against multi-threaded
  filesystems.

* Only occurs in the write path, probably only H2 is affected.

sys/kern/vfs_bio.c
sys/kern/vfs_cluster.c

index 6dd2959..6423445 100644 (file)
@@ -2755,7 +2755,8 @@ findblk(struct vnode *vp, off_t loffset, int flags)
  *
  *     Similar to getblk() except only returns the buffer if it is
  *     B_CACHE and requires no other manipulation.  Otherwise NULL
- *     is returned.
+ *     is returned.  NULL is also returned if GETBLK_NOWAIT is set
+ *     and the getblk() would block.
  *
  *     If B_RAM is set the buffer might be just fine, but we return
  *     NULL anyway because we want the code to fall through to the
index 95adc83..65710bc 100644 (file)
@@ -1550,6 +1550,9 @@ cluster_wbuild(struct vnode *vp, struct buf **bpp,
  * Only pre-existing buffers whos block size matches blksize are collected.
  * (this is primarily because HAMMER1 uses varying block sizes and we don't
  * want to override its choices).
+ *
+ * This code will not try to collect buffers that it cannot lock, otherwise
+ * it might deadlock against SMP-friendly filesystems.
  */
 static struct cluster_save *
 cluster_collectbufs(cluster_cache_t *cc, struct vnode *vp,
@@ -1572,7 +1575,8 @@ cluster_collectbufs(cluster_cache_t *cc, struct vnode *vp,
             i < len;
             (loffset += blksize), i++) {
                bp = getcacheblk(vp, loffset,
-                                last_bp->b_bcount, GETBLK_SZMATCH);
+                                last_bp->b_bcount, GETBLK_SZMATCH |
+                                                   GETBLK_NOWAIT);
                buflist->bs_children[i] = bp;
                if (bp == NULL) {
                        j = i + 1;