public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Reiter <s.reiter@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 1/3] tools: add mmap_buffer module
Date: Wed, 28 Apr 2021 18:06:53 +0200	[thread overview]
Message-ID: <20210428160655.29941-2-s.reiter@proxmox.com> (raw)
In-Reply-To: <20210428160655.29941-1-s.reiter@proxmox.com>

Can allocate a large buffer of fixed-size chunks via a memory mapped
file, to allow the kernel to easily swap these pages back to disk on
memory pressure.

Designed to be used with an LRU cache or similar.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 src/tools.rs             |   1 +
 src/tools/mmap_buffer.rs | 113 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 114 insertions(+)
 create mode 100644 src/tools/mmap_buffer.rs

diff --git a/src/tools.rs b/src/tools.rs
index 08af55e5..8f22e6c8 100644
--- a/src/tools.rs
+++ b/src/tools.rs
@@ -34,6 +34,7 @@ pub mod json;
 pub mod logrotate;
 pub mod loopdev;
 pub mod lru_cache;
+pub mod mmap_buffer;
 pub mod nom;
 pub mod runtime;
 pub mod serde_filter;
diff --git a/src/tools/mmap_buffer.rs b/src/tools/mmap_buffer.rs
new file mode 100644
index 00000000..764303c2
--- /dev/null
+++ b/src/tools/mmap_buffer.rs
@@ -0,0 +1,113 @@
+//! Provides a buffer allocation API using a memory mapped file.
+use anyhow::{bail, Error};
+use nix::sys::mman;
+use nix::unistd::{ftruncate, unlink};
+use std::sync::{Arc, Mutex};
+
+use proxmox::tools::fs::{make_tmp_file, CreateOptions};
+use proxmox::tools::mmap::Mmap;
+
+pub struct MmapBufferEntry {
+    pub buf: &'static mut [u8],
+    entry_idx: usize,
+    parent: Arc<MmapBuffer>,
+}
+
+pub struct MmapBuffer {
+    entry_size: usize,
+    entry_count: usize,
+    bitmap: Mutex<Vec<u64>>,
+    mmap: Mmap<u8>,
+}
+
+impl MmapBuffer {
+    /// Create a new mmap buffer containing _count [u8] slices of length _size. This requires root
+    /// to allocate the temp file securely.
+    pub fn new(entry_size: usize, entry_count: usize) -> Result<Arc<Self>, Error> {
+        let bytes = entry_size * entry_count;
+        let (fd, path) = make_tmp_file(
+            "/var/cache/proxmox-backup/mmap-buffer",
+            CreateOptions::default(),
+        )?;
+        unlink(&path)?;
+        ftruncate(fd.0, bytes as i64)?;
+        let mmap = unsafe {
+            Mmap::map_fd(
+                fd.0,
+                0,
+                bytes,
+                mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE,
+                mman::MapFlags::MAP_SHARED,
+            )?
+        };
+        Ok(Arc::new(Self {
+            entry_size,
+            entry_count,
+            bitmap: Mutex::new(vec![0; (entry_count+63)/64]),
+            mmap,
+        }))
+    }
+
+    #[inline]
+    fn bitmap_idx(lin: usize) -> (usize, u64) {
+        let idx = lin / 64;
+        let mask = 1 << (lin & 0x3F);
+        (idx, mask)
+    }
+
+    /// Returns one of the slices available in the buffer. The slice will be returned to this
+    /// instance on drop. If entry_count slices are already handed out, this function will fail.
+    pub fn allocate(self: &Arc<Self>) -> Result<MmapBufferEntry, Error> {
+        let bitmap = &mut (*self.bitmap.lock().unwrap());
+        for i in 0..self.entry_count as usize {
+            let (idx, mask) = Self::bitmap_idx(i);
+            if (bitmap[idx] & mask) == 0 {
+                bitmap[idx] |= mask;
+                let offset = self.entry_size * i;
+                let end = offset + self.entry_size;
+                // Safety:
+                // * mut: self.bitmap prevents multiple borrows on one region
+                // * 'static: the MmapBufferEntry contains a clone of the Arc pointing to ourselves
+                let data: &'static mut [u8] = unsafe {
+                    std::slice::from_raw_parts_mut(
+                        self.mmap[offset..end].as_ptr() as *mut u8,
+                        end - offset,
+                    )
+                };
+                let me = Arc::clone(&self);
+                return Ok(MmapBufferEntry {
+                    buf: data,
+                    entry_idx: i,
+                    parent: me,
+                });
+            }
+        }
+
+        bail!("mmap-buffer: cannot allocate more entries in buffer");
+    }
+
+    fn deallocate(self: &Arc<Self>, offset: usize) {
+        let bitmap = &mut (*self.bitmap.lock().unwrap());
+        let (idx, mask) = Self::bitmap_idx(offset);
+        bitmap[idx] &= !mask;
+    }
+}
+
+impl Drop for MmapBufferEntry {
+    fn drop(&mut self) {
+        self.parent.deallocate(self.entry_idx);
+    }
+}
+
+impl std::ops::Deref for MmapBufferEntry {
+    type Target = [u8];
+    fn deref(&self) -> &Self::Target {
+        self.buf
+    }
+}
+
+impl std::ops::DerefMut for MmapBufferEntry {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.buf
+    }
+}
-- 
2.20.1





  reply	other threads:[~2021-04-28 16:07 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-28 16:06 [pbs-devel] [PATCH 0/3] Add cache for live-restore Stefan Reiter
2021-04-28 16:06 ` Stefan Reiter [this message]
2021-04-28 16:06 ` [pbs-devel] [PATCH proxmox-backup 2/3] RemoteChunkReader: add LRU cached variant Stefan Reiter
2021-04-28 16:06 ` [pbs-devel] [PATCH proxmox-backup-qemu 3/3] access: use bigger cache and LRU chunk reader Stefan Reiter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210428160655.29941-2-s.reiter@proxmox.com \
    --to=s.reiter@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal