diff --git a/rust/hg-core/src/revlog/mod.rs b/rust/hg-core/src/revlog/mod.rs
index 6b7ffa3f9199fab91e57d2f6425d2a28f45b4682_cnVzdC9oZy1jb3JlL3NyYy9yZXZsb2cvbW9kLnJz..84b5802ba7d3927e8beb1a3e0d275e41806417e9_cnVzdC9oZy1jb3JlL3NyYy9yZXZsb2cvbW9kLnJz 100644
--- a/rust/hg-core/src/revlog/mod.rs
+++ b/rust/hg-core/src/revlog/mod.rs
@@ -503,6 +503,10 @@
             {
                 let size = store_vfs.file_size(&file)?;
                 if size >= threshold {
+                    // TODO madvise populate read in a background thread
+                    let mut mmap_options = MmapOptions::new();
+                    // This does nothing on platforms where it's not defined
+                    mmap_options.populate();
                     // Safety is "enforced" by locks and assuming other
                     // processes are well-behaved. If any misbehaving or
                     // malicious process does touch the index, it could lead
@@ -510,7 +514,7 @@
                     // `mmap`, though some platforms have some ways of
                     // mitigating.
                     // TODO linux: set the immutable flag with `chattr(1)`?
-                    let mmap = unsafe { MmapOptions::new().map(&file) }
+                    let mmap = unsafe { mmap_options.map(&file) }
                         .when_reading_file(index_path)?;
                     Some(Box::new(mmap) as IndexData)
                 } else {
diff --git a/rust/hg-core/src/revlog/options.rs b/rust/hg-core/src/revlog/options.rs
index 6b7ffa3f9199fab91e57d2f6425d2a28f45b4682_cnVzdC9oZy1jb3JlL3NyYy9yZXZsb2cvb3B0aW9ucy5ycw==..84b5802ba7d3927e8beb1a3e0d275e41806417e9_cnVzdC9oZy1jb3JlL3NyYy9yZXZsb2cvb3B0aW9ucy5ycw== 100644
--- a/rust/hg-core/src/revlog/options.rs
+++ b/rust/hg-core/src/revlog/options.rs
@@ -103,6 +103,20 @@
     }
 }
 
+/// Technically only Linux 2.5.46+ has `MAP_POPULATE` and only `2.6.23` on
+/// private mappings, but if you're using such ancient Linux, you have other
+/// problems.
+#[cfg(target_os = "linux")]
+const fn can_populate_mmap() -> bool {
+    true
+}
+
+/// There is a of populating mmaps for Windows, but it would need testing.
+#[cfg(not(target_os = "linux"))]
+const fn can_populate_mmap() {
+    false
+}
+
 #[derive(Debug, Clone, Copy, PartialEq)]
 /// Holds configuration values about how the revlog data is read
 pub struct RevlogDataConfig {
@@ -155,6 +169,20 @@
             }
         }
 
+        // Use mmap if requested, or by default if we can fully populate it
+        let mmap_index = config
+            .get_option_no_default(b"storage", b"revlog.mmap.index")?
+            .unwrap_or(can_populate_mmap());
+        if mmap_index {
+            if let Some(mmap_index_threshold) = config.get_byte_size(
+                b"storage",
+                b"revlog.mmap.index:size-threshold",
+            )? {
+                // Only mmap if above the requested size threshold
+                data_config.mmap_index_threshold = Some(mmap_index_threshold);
+            }
+        }
+
         if let Some(mmap_index_threshold) = config
             .get_byte_size(b"storage", b"revlog.mmap.index:size-threshold")?
         {