summaryrefslogtreecommitdiff
path: root/ext2fs/pager.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext2fs/pager.c')
-rw-r--r--ext2fs/pager.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/ext2fs/pager.c b/ext2fs/pager.c
index c55107a9..a7801bea 100644
--- a/ext2fs/pager.c
+++ b/ext2fs/pager.c
@@ -83,6 +83,10 @@ do { pthread_spin_lock (&ext2s_pager_stats.lock); \
#define STAT_INC(field) /* nop */0
#endif /* STATS */
+#ifndef EXT2_BULK_MAX_BLOCKS
+#define EXT2_BULK_MAX_BLOCKS 128
+#endif /* EXT2_BULK_MAX_BLOCKS */
+
static void
disk_cache_info_free_push (struct disk_cache_info *p);
@@ -378,6 +382,149 @@ pending_blocks_add (struct pending_blocks *pb, block_t block)
pb->num++;
return 0;
}
+
+/* Bulk write across [offset, offset + length). We coalesce strictly
+ consecutive blocks into a single device write. The run ends on the
+ first non-consecutive block or when we hit the configured cap.
+ We report partial progress via *written (page-aligned). */
+static error_t
+file_pager_write_pages (struct node *node,
+ vm_offset_t offset,
+ vm_address_t buf,
+ vm_size_t length,
+ vm_size_t *written)
+{
+ error_t err = 0;
+ vm_size_t done = 0; /* bytes successfully issued to the store */
+ pthread_rwlock_t *lock = &diskfs_node_disknode (node)->alloc_lock;
+ const unsigned max_blocks = EXT2_BULK_MAX_BLOCKS;
+
+ /* Persisted lookahead block across runs: if non-zero, it is the first
+ block of the next coalesced run and we won't re-find it. */
+ block_t blk_peek = 0;
+
+ if (written)
+ *written = 0;
+
+ while (done < length)
+ {
+ pthread_rwlock_rdlock (lock);
+
+ /* Recompute clipping against allocsize each iteration. */
+ vm_size_t left = length - done;
+ if (offset + done >= node->allocsize)
+ {
+ pthread_rwlock_unlock (lock);
+ break;
+ }
+ if (offset + done + left > node->allocsize)
+ left = node->allocsize - (offset + done);
+ if (left == 0)
+ {
+ pthread_rwlock_unlock (lock);
+ break;
+ }
+
+ /* Build one run of strictly consecutive on-disk blocks. */
+ struct pending_blocks pb;
+ vm_size_t built = 0;
+ vm_size_t blocks_built = 0;
+ block_t prev = 0;
+ block_t blk = 0;
+
+ pending_blocks_init (&pb, (void *) (buf + done));
+
+ while (blocks_built < max_blocks && built < left)
+ {
+ /* Use peeked block if we have one; otherwise look it up. */
+ if (blk_peek)
+ {
+ blk = blk_peek;
+ }
+ else
+ {
+ error_t ferr =
+ find_block (node, offset + done + built, &blk, &lock);
+ if (ferr)
+ {
+ err = ferr;
+ break;
+ }
+ }
+
+ assert_backtrace (blk);
+
+ if (prev != 0 && blk != prev + 1)
+ {
+ /* Non-consecutive: keep BLK for the next outer run. */
+ blk_peek = blk;
+ break;
+ }
+
+ /* Extend the run by one block. We are consuming BLK now. */
+ error_t ferr = pending_blocks_add (&pb, blk);
+ if (ferr)
+ {
+ err = ferr;
+ break;
+ }
+
+ prev = blk;
+ built += block_size;
+ blocks_built++;
+
+ /* We consumed the peeked block; clear it so we fetch the next. */
+ blk_peek = 0;
+ }
+
+ /* Flush exactly one coalesced run; even if the loop broke early,
+ we may have a valid prefix to push. */
+ error_t werr = pending_blocks_write (&pb);
+ if (!err)
+ err = werr;
+
+ pthread_rwlock_unlock (lock);
+
+ /* Advance only by what we actually enumerated and flushed. */
+ done += built;
+
+ /* Stop on error. */
+ if (err)
+ break;
+ }
+
+ /* We must not be "holding" a peeked block on return unless we stopped
+ due to an error. */
+ assert_backtrace (blk_peek == 0 || err);
+
+ if (written)
+ {
+ vm_size_t w = done;
+ if (w > length)
+ w = length;
+ /* libpager expects progress aligned to whole pages. */
+ w -= (w % vm_page_size);
+ *written = w;
+ }
+
+ return err;
+}
+
+/* Strong override: only FILE_DATA uses bulk; others keep per-page path. */
+error_t
+pager_write_pages (struct user_pager_info *pager,
+ vm_offset_t offset,
+ vm_address_t data,
+ vm_size_t length,
+ vm_size_t *written)
+{
+ /* libpager will just hand this off to the pager_write_page. */
+ if (pager->type != FILE_DATA)
+ return EOPNOTSUPP;
+
+ return file_pager_write_pages (pager->node, offset, data, length, written);
+}
+
/* Write one page for the pager backing NODE, at OFFSET, into BUF. This
may need to write several filesystem blocks to satisfy one page, and tries