|
@@ -1837,19 +1837,37 @@ static int __logfs_truncate(struct inode *inode, u64 size)
|
|
|
return logfs_truncate_direct(inode, size);
|
|
|
}
|
|
|
|
|
|
-int logfs_truncate(struct inode *inode, u64 size)
|
|
|
+/*
|
|
|
+ * Truncate, by changing the segment file, can consume a fair amount
|
|
|
+ * of resources. So back off from time to time and do some GC.
|
|
|
+ * 8 or 2048 blocks should be well within safety limits even if
|
|
|
+ * every single block resided in a different segment.
|
|
|
+ */
|
|
|
+#define TRUNCATE_STEP (8 * 1024 * 1024)
|
|
|
+int logfs_truncate(struct inode *inode, u64 target)
|
|
|
{
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
- int err;
|
|
|
+ u64 size = i_size_read(inode);
|
|
|
+ int err = 0;
|
|
|
|
|
|
- logfs_get_wblocks(sb, NULL, 1);
|
|
|
- err = __logfs_truncate(inode, size);
|
|
|
- if (!err)
|
|
|
- err = __logfs_write_inode(inode, 0);
|
|
|
- logfs_put_wblocks(sb, NULL, 1);
|
|
|
+ size = ALIGN(size, TRUNCATE_STEP);
|
|
|
+ while (size > target) {
|
|
|
+ if (size > TRUNCATE_STEP)
|
|
|
+ size -= TRUNCATE_STEP;
|
|
|
+ else
|
|
|
+ size = 0;
|
|
|
+ if (size < target)
|
|
|
+ size = target;
|
|
|
+
|
|
|
+ logfs_get_wblocks(sb, NULL, 1);
|
|
|
+ err = __logfs_truncate(inode, target);
|
|
|
+ if (!err)
|
|
|
+ err = __logfs_write_inode(inode, 0);
|
|
|
+ logfs_put_wblocks(sb, NULL, 1);
|
|
|
+ }
|
|
|
|
|
|
if (!err)
|
|
|
- err = vmtruncate(inode, size);
|
|
|
+ err = vmtruncate(inode, target);
|
|
|
|
|
|
/* I don't trust error recovery yet. */
|
|
|
WARN_ON(err);
|