From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id CF0A877AD1 for ; Wed, 28 Apr 2021 18:07:06 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 9527F119DD for ; Wed, 28 Apr 2021 18:07:06 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 1C77A119A0 for ; Wed, 28 Apr 2021 18:07:05 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id E48FA4645B for ; Wed, 28 Apr 2021 18:07:04 +0200 (CEST) From: Stefan Reiter To: pbs-devel@lists.proxmox.com Date: Wed, 28 Apr 2021 18:06:53 +0200 Message-Id: <20210428160655.29941-2-s.reiter@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210428160655.29941-1-s.reiter@proxmox.com> References: <20210428160655.29941-1-s.reiter@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.017 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [PATCH proxmox-backup 1/3] tools: add mmap_buffer module X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 28 Apr 2021 16:07:06 -0000 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 --- 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, +} + +pub struct MmapBuffer { + entry_size: usize, + entry_count: usize, + bitmap: Mutex>, + mmap: Mmap, +} + +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, 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) -> Result { + 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, 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