⬢ DragonFlyBSD Kernel Audit
DF-3215 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hammer/hammer_redo.c b/sys/vfs/hammer/hammer_redo.c
index fc5a4dc..fb8ac84 100644
--- a/sys/vfs/hammer/hammer_redo.c
+++ b/sys/vfs/hammer/hammer_redo.c
@@ -87,8 +87,23 @@ hammer_generate_redo(hammer_transaction_t trans, hammer_inode_t ip,
 	hammer_lock_ex(&hmp->undo_lock);
 
 	/* undo had better not roll over (loose test) */
-	if (hammer_undo_space(trans) < len + HAMMER_BUFSIZE*3)
-		hpanic("insufficient UNDO/REDO FIFO space for redo!");
+	if (hammer_undo_space(trans) < len + HAMMER_BUFSIZE*3) {
+		/*
+		 * The UNDO/REDO FIFO is exhausted because the flusher has
+		 * not yet reclaimed space (the on-disk first_offset lags at
+		 * least one full finalize cycle behind the frontend).
+		 *
+		 * REDO is an optional optimization, so fail the operation
+		 * gracefully instead of panicking the kernel.  The frontend
+		 * write path also disables REDO for the inode under FIFO
+		 * pressure (hammer_vnops.c), causing the next fsync() to
+		 * fall back to a full flush.
+		 */
+		error = ENOSPC;
+		hammer_modify_volume_done(root_volume);
+		hammer_unlock(&hmp->undo_lock);
+		return(error);
+	}
 
 	/*
 	 * Loop until the undo for the entire range has been laid down.
diff --git a/sys/vfs/hammer/hammer_vnops.c b/sys/vfs/hammer/hammer_vnops.c
index c8150ed..6186f17 100644
--- a/sys/vfs/hammer/hammer_vnops.c
+++ b/sys/vfs/hammer/hammer_vnops.c
@@ -741,7 +741,13 @@ hammer_vop_write(struct vop_write_args *ap)
 		 * flushed.
 		 */
 		if ((ip->flags & HAMMER_INODE_REDO) && error == 0) {
-			if (ip->redo_count < hammer_limit_redo) {
+			int64_t undospace;
+
+			hammer_lock_sh(&hmp->undo_lock);
+			undospace = hammer_undo_space(&trans);
+			hammer_unlock(&hmp->undo_lock);
+			if (ip->redo_count < hammer_limit_redo &&
+			    undospace > (int64_t)hammer_limit_redo * 2) {
 				bp->b_flags |= B_VFSFLAG1;
 				error = hammer_generate_redo(&trans, ip,
 						     base_offset + offset,
@@ -749,6 +755,12 @@ hammer_vop_write(struct vop_write_args *ap)
 						     bp->b_data + offset,
 						     (size_t)n);
 			} else {
+				/*
+				 * Per-inode budget exhausted or the
+				 * UNDO/REDO FIFO is running low: disable
+				 * REDO until the next fsync() re-enables
+				 * it (full-flush fallback).
+				 */
 				ip->flags &= ~HAMMER_INODE_REDO;
 			}
 		}