procfs_core/
meminfo.rs

1use super::{expect, from_str, ProcResult};
2#[cfg(feature = "serde1")]
3use serde::{Deserialize, Serialize};
4use std::{collections::HashMap, io};
5
6fn convert_to_kibibytes(num: u64, unit: &str) -> ProcResult<u64> {
7    match unit {
8        "B" => Ok(num),
9        "KiB" | "kiB" | "kB" | "KB" => Ok(num * 1024),
10        "MiB" | "miB" | "MB" | "mB" => Ok(num * 1024 * 1024),
11        "GiB" | "giB" | "GB" | "gB" => Ok(num * 1024 * 1024 * 1024),
12        unknown => Err(build_internal_error!(format!("Unknown unit type {}", unknown))),
13    }
14}
15
16/// This  struct  reports  statistics about memory usage on the system, based on
17/// the `/proc/meminfo` file.
18///
19/// It is used by `free(1)` to report the amount of free and used memory (both
20/// physical  and  swap)  on  the  system  as well as the shared memory and
21/// buffers used by the kernel.  Each struct member is generally reported in
22/// bytes, but a few are unitless values.
23///
24/// Except as noted below, all of the fields have been present since at least
25/// Linux 2.6.0.  Some fields are optional and are present only if the kernel
26/// was configured with various options; those dependencies are noted in the list.
27///
28/// **Notes**
29///
30/// While the file shows kilobytes (kB; 1 kB equals 1000 B),
31/// it is actually kibibytes (KiB; 1 KiB equals 1024 B).
32///
33/// All sizes are converted to bytes. Unitless values, like `hugepages_total` are not affected.
34///
35/// This imprecision in /proc/meminfo is known,
36/// but is not corrected due to legacy concerns -
37/// programs rely on /proc/meminfo to specify size with the "kB" string.
38///
39/// New fields to this struct may be added at any time (even without a major or minor semver bump).
40#[derive(Debug, Clone)]
41#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
42#[allow(non_snake_case)]
43#[non_exhaustive]
44pub struct Meminfo {
45    /// Total usable RAM (i.e., physical RAM minus a few reserved bits and the kernel binary code).
46    pub mem_total: u64,
47    /// The sum of [LowFree](#structfield.low_free) + [HighFree](#structfield.high_free).
48    pub mem_free: u64,
49    /// An estimate of how much memory is available for starting new applications, without swapping.
50    ///
51    /// (since Linux 3.14)
52    pub mem_available: Option<u64>,
53    /// Relatively temporary storage for raw disk blocks that shouldn't get tremendously large (20MB or so).
54    pub buffers: u64,
55    /// In-memory cache for files read from the disk (the page cache).  Doesn't include SwapCached.
56    pub cached: u64,
57    /// Memory  that  once  was  swapped  out, is swapped back in but still also is in the swap
58    /// file.
59    ///
60    /// (If memory pressure is high, these pages don't need to be swapped out again
61    /// because they are already in the swap file.  This saves I/O.)
62    pub swap_cached: u64,
63    /// Memory that has been used more recently and usually not reclaimed unless absolutely
64    /// necessary.
65    pub active: u64,
66    /// Memory which has been less recently used.  It is more eligible to be reclaimed for other
67    /// purposes.
68    pub inactive: u64,
69    /// [To be documented.]
70    ///
71    /// (since Linux 2.6.28)
72    pub active_anon: Option<u64>,
73    /// [To be documented.]
74    ///
75    /// (since Linux 2.6.28)
76    pub inactive_anon: Option<u64>,
77    /// [To be documented.]
78    ///
79    /// (since Linux 2.6.28)
80    pub active_file: Option<u64>,
81    /// [To be documented.]
82    ///
83    /// (since Linux 2.6.28)
84    pub inactive_file: Option<u64>,
85    /// [To be documented.]
86    ///
87    /// (From Linux 2.6.28 to 2.6.30, CONFIG_UNEVICTABLE_LRU was required.)
88    pub unevictable: Option<u64>,
89    /// [To be documented.]
90    ///
91    /// (From Linux 2.6.28 to 2.6.30, CONFIG_UNEVICTABLE_LRU was required.)
92    pub mlocked: Option<u64>,
93    /// Total amount of highmem.
94    ///
95    /// Highmem is all memory above ~860MB of physical  memory.  Highmem areas are for use by
96    /// user-space programs, or for the page cache.  The kernel must use tricks to access this
97    /// memory, making it slower to access than lowmem.
98    ///
99    /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
100    pub high_total: Option<u64>,
101    /// Amount of free highmem.
102    ///
103    /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
104    pub high_free: Option<u64>,
105    /// Total amount of lowmem.
106    ///
107    /// Lowmem is memory which can be used for every thing  that highmem can be used for,
108    /// but it is also available for the kernel's use for its own data structures.
109    /// Among many other things, it is where everything from Slab is allocated.
110    /// Bad things happen when you're out of lowmem.
111    ///
112    /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
113    pub low_total: Option<u64>,
114    /// Amount of free lowmem.
115    ///
116    /// (Starting with Linux 2.6.19, CONFIG_HIGHMEM is required.)
117    pub low_free: Option<u64>,
118    /// [To be documented.]
119    ///
120    /// (since Linux 2.6.29.  CONFIG_MMU is required.)
121    pub mmap_copy: Option<u64>,
122    /// Total amount of swap space available.
123    pub swap_total: u64,
124    /// Amount of swap space that is currently unused.
125    pub swap_free: u64,
126    /// Memory which is waiting to get written back to the disk.
127    pub dirty: u64,
128    /// Memory which is actively being written back to the disk.
129    pub writeback: u64,
130    /// Non-file backed pages mapped into user-space page tables.
131    ///
132    /// (since Linux 2.6.18)
133    pub anon_pages: Option<u64>,
134    /// Files which have been mapped into memory (with mmap(2)), such as libraries.
135    pub mapped: u64,
136    /// Amount of memory consumed in tmpfs(5) filesystems.
137    ///
138    /// (since Linux 2.6.32)
139    pub shmem: Option<u64>,
140    /// In-kernel data structures cache.
141    pub slab: u64,
142    /// Part of Slab, that can be reclaimed on memory pressure.
143    ///
144    /// (since Linux 2.6.19)
145    pub s_reclaimable: Option<u64>,
146    /// Part of Slab, that cannot be reclaimed on memory pressure.
147    ///
148    /// (since Linux 2.6.19)
149    pub s_unreclaim: Option<u64>,
150    /// Amount of memory allocated to kernel stacks.
151    ///
152    /// (since Linux 2.6.32)
153    pub kernel_stack: Option<u64>,
154    /// Amount of memory dedicated to the lowest level of page tables.
155    ///
156    /// (since Linux 2.6.18)
157    pub page_tables: Option<u64>,
158    /// Amount of memory allocated for seconary page tables. This currently includes KVM mmu
159    /// allocations on x86 and arm64.
160    ///
161    /// (since Linux 6.1)
162    pub secondary_page_tables: Option<u64>,
163    /// [To be documented.]
164    ///
165    /// (CONFIG_QUICKLIST is required.  Since Linux 2.6.27)
166    pub quicklists: Option<u64>,
167    /// NFS pages sent to the server, but not yet committed to stable storage.
168    ///
169    /// (since Linux 2.6.18)
170    pub nfs_unstable: Option<u64>,
171    /// Memory used for block device "bounce buffers".
172    ///
173    /// (since Linux 2.6.18)
174    pub bounce: Option<u64>,
175    /// Memory used by FUSE for temporary writeback buffers.
176    ///
177    /// (since Linux 2.6.26)
178    pub writeback_tmp: Option<u64>,
179    /// This is the total amount of memory currently available to be allocated on the system,
180    /// expressed  in bytes.
181    ///
182    /// This  limit  is adhered  to  only if strict overcommit
183    /// accounting is enabled (mode 2 in /proc/sys/vm/overcommit_memory).  The limit is calculated
184    /// according to the formula described under /proc/sys/vm/overcommit_memory.  For further
185    /// details, see the kernel source  file
186    /// [Documentation/vm/overcommit-accounting](https://www.kernel.org/doc/Documentation/vm/overcommit-accounting).
187    ///
188    /// (since Linux 2.6.10)
189    pub commit_limit: Option<u64>,
190    /// The  amount of memory presently allocated on the system.
191    ///
192    /// The committed memory is a sum of all of the memory which has been allocated
193    /// by processes, even if it has not been "used" by them as of yet.  A process which allocates 1GB of memory  (using  malloc(3)
194    /// or  similar),  but  touches only 300MB of that memory will show up as using only 300MB of memory even if it has the address space
195    /// allocated for the entire 1GB.
196    ///
197    /// This 1GB is memory which has been "committed" to by the VM and can be used at any  time  by  the  allocating  application.   With
198    /// strict  overcommit  enabled  on  the  system  (mode 2 in /proc/sys/vm/overcommit_memory), allocations which would exceed the Committed_AS
199    /// mitLimit will not be permitted.  This is useful if one needs to guarantee that processes will not fail due to lack of memory once
200    /// that memory has been successfully allocated.
201    pub committed_as: u64,
202    /// Total size of vmalloc memory area.
203    pub vmalloc_total: u64,
204    /// Amount of vmalloc area which is used.
205    pub vmalloc_used: u64,
206    /// Largest contiguous block of vmalloc area which is free.
207    pub vmalloc_chunk: u64,
208    /// [To be documented.]
209    ///
210    /// (CONFIG_MEMORY_FAILURE is required.  Since Linux 2.6.32)
211    pub hardware_corrupted: Option<u64>,
212    /// Non-file backed huge pages mapped into user-space page tables.
213    ///
214    /// (CONFIG_TRANSPARENT_HUGEPAGE is required.  Since Linux 2.6.38)
215    pub anon_hugepages: Option<u64>,
216    /// Memory used by shared memory (shmem) and tmpfs(5) allocated with huge pages
217    ///
218    /// (CONFIG_TRANSPARENT_HUGEPAGE is required.  Since Linux 4.8)
219    pub shmem_hugepages: Option<u64>,
220    /// Shared memory mapped into user space with huge pages.
221    ///
222    /// (CONFIG_TRANSPARENT_HUGEPAGE is required.  Since Linux 4.8)
223    pub shmem_pmd_mapped: Option<u64>,
224    /// Total CMA (Contiguous Memory Allocator) pages.
225    ///
226    /// (CONFIG_CMA is required.  Since Linux 3.1)
227    pub cma_total: Option<u64>,
228    /// Free CMA (Contiguous Memory Allocator) pages.
229    ///
230    /// (CONFIG_CMA is required.  Since Linux 3.1)
231    pub cma_free: Option<u64>,
232    /// The size of the pool of huge pages.
233    ///
234    /// CONFIG_HUGETLB_PAGE is required.)
235    pub hugepages_total: Option<u64>,
236    /// The number of huge pages in the pool that are not yet allocated.
237    ///
238    /// (CONFIG_HUGETLB_PAGE is required.)
239    pub hugepages_free: Option<u64>,
240    /// This is the number of huge pages for which a commitment to allocate from the pool has been
241    /// made, but no allocation has yet been made.
242    ///
243    /// These reserved huge pages guarantee that an application will be able  to  allocate  a
244    /// huge page from the pool of huge pages at fault time.
245    ///
246    /// (CONFIG_HUGETLB_PAGE  is  required.  Since Linux 2.6.17)
247    pub hugepages_rsvd: Option<u64>,
248    /// This is the number of huge pages in the pool above the value in /proc/sys/vm/nr_hugepages.
249    ///
250    /// The maximum number of surplus huge pages is controlled by /proc/sys/vm/nr_overcommit_hugepages.
251    ///
252    /// (CONFIG_HUGETLB_PAGE  is  required.  Since Linux 2.6.24)
253    pub hugepages_surp: Option<u64>,
254    /// The size of huge pages.
255    ///
256    /// (CONFIG_HUGETLB_PAGE is required.)
257    pub hugepagesize: Option<u64>,
258    /// Number of bytes of RAM linearly mapped by kernel in 4kB pages.  (x86.)
259    ///
260    /// (since Linux 2.6.27)
261    pub direct_map_4k: Option<u64>,
262    /// Number of bytes of RAM linearly mapped by kernel in 4MB pages.
263    ///
264    /// (x86 with CONFIG_X86_64 or CONFIG_X86_PAE enabled.  Since Linux 2.6.27)
265    pub direct_map_4M: Option<u64>,
266    /// Number of bytes of RAM linearly mapped by kernel in 2MB pages.
267    ///
268    /// (x86 with neither CONFIG_X86_64 nor CONFIG_X86_PAE enabled.  Since Linux 2.6.27)
269    pub direct_map_2M: Option<u64>,
270    /// (x86 with CONFIG_X86_64 and CONFIG_X86_DIRECT_GBPAGES enabled.  Since Linux 2.6.27)
271    pub direct_map_1G: Option<u64>,
272
273    /// needs documentation
274    pub hugetlb: Option<u64>,
275
276    /// Memory allocated to the per-cpu alloctor used to back per-cpu allocations.
277    ///
278    /// This stat excludes the cost of metadata.
279    pub per_cpu: Option<u64>,
280
281    /// Kernel allocations that the kernel will attempt to reclaim under memory pressure.
282    ///
283    /// Includes s_reclaimable, and other direct allocations with a shrinker.
284    pub k_reclaimable: Option<u64>,
285
286    /// Undocumented field
287    ///
288    /// (CONFIG_TRANSPARENT_HUGEPAGE is requried.  Since Linux 5.4)
289    pub file_pmd_mapped: Option<u64>,
290
291    /// Undocumented field
292    ///
293    /// (CONFIG_TRANSPARENT_HUGEPAGE is required.  Since Linux 5.4)
294    pub file_huge_pages: Option<u64>,
295
296    /// Memory consumed by the zswap backend (compressed size).
297    ///
298    /// (CONFIG_ZSWAP is required.  Since Linux 5.19)
299    pub z_swap: Option<u64>,
300
301    /// Amount of anonymous memory stored in zswap (original size).
302    ///
303    /// (CONFIG_ZSWAP is required.  Since Linux 5.19)
304    pub z_swapped: Option<u64>,
305}
306
307impl super::FromBufRead for Meminfo {
308    fn from_buf_read<R: io::BufRead>(r: R) -> ProcResult<Self> {
309        let mut map = HashMap::new();
310
311        for line in r.lines() {
312            let line = expect!(line);
313            if line.is_empty() {
314                continue;
315            }
316            let mut s = line.split_whitespace();
317            let field = expect!(s.next(), "no field");
318            let value = expect!(s.next(), "no value");
319            let unit = s.next(); // optional
320
321            let value = from_str!(u64, value);
322
323            let value = if let Some(unit) = unit {
324                convert_to_kibibytes(value, unit)?
325            } else {
326                value
327            };
328
329            map.insert(field[..field.len() - 1].to_string(), value);
330        }
331
332        // use 'remove' to move the value out of the hashmap
333        // if there's anything still left in the map at the end, that
334        // means we probably have a bug/typo, or are out-of-date
335        let meminfo = Meminfo {
336            mem_total: expect!(map.remove("MemTotal")),
337            mem_free: expect!(map.remove("MemFree")),
338            mem_available: map.remove("MemAvailable"),
339            buffers: expect!(map.remove("Buffers")),
340            cached: expect!(map.remove("Cached")),
341            swap_cached: expect!(map.remove("SwapCached")),
342            active: expect!(map.remove("Active")),
343            inactive: expect!(map.remove("Inactive")),
344            active_anon: map.remove("Active(anon)"),
345            inactive_anon: map.remove("Inactive(anon)"),
346            active_file: map.remove("Active(file)"),
347            inactive_file: map.remove("Inactive(file)"),
348            unevictable: map.remove("Unevictable"),
349            mlocked: map.remove("Mlocked"),
350            high_total: map.remove("HighTotal"),
351            high_free: map.remove("HighFree"),
352            low_total: map.remove("LowTotal"),
353            low_free: map.remove("LowFree"),
354            mmap_copy: map.remove("MmapCopy"),
355            swap_total: expect!(map.remove("SwapTotal")),
356            swap_free: expect!(map.remove("SwapFree")),
357            dirty: expect!(map.remove("Dirty")),
358            writeback: expect!(map.remove("Writeback")),
359            anon_pages: map.remove("AnonPages"),
360            mapped: expect!(map.remove("Mapped")),
361            shmem: map.remove("Shmem"),
362            slab: expect!(map.remove("Slab")),
363            s_reclaimable: map.remove("SReclaimable"),
364            s_unreclaim: map.remove("SUnreclaim"),
365            kernel_stack: map.remove("KernelStack"),
366            page_tables: map.remove("PageTables"),
367            secondary_page_tables: map.remove("SecPageTables"),
368            quicklists: map.remove("Quicklists"),
369            nfs_unstable: map.remove("NFS_Unstable"),
370            bounce: map.remove("Bounce"),
371            writeback_tmp: map.remove("WritebackTmp"),
372            commit_limit: map.remove("CommitLimit"),
373            committed_as: expect!(map.remove("Committed_AS")),
374            vmalloc_total: expect!(map.remove("VmallocTotal")),
375            vmalloc_used: expect!(map.remove("VmallocUsed")),
376            vmalloc_chunk: expect!(map.remove("VmallocChunk")),
377            hardware_corrupted: map.remove("HardwareCorrupted"),
378            anon_hugepages: map.remove("AnonHugePages"),
379            shmem_hugepages: map.remove("ShmemHugePages"),
380            shmem_pmd_mapped: map.remove("ShmemPmdMapped"),
381            cma_total: map.remove("CmaTotal"),
382            cma_free: map.remove("CmaFree"),
383            hugepages_total: map.remove("HugePages_Total"),
384            hugepages_free: map.remove("HugePages_Free"),
385            hugepages_rsvd: map.remove("HugePages_Rsvd"),
386            hugepages_surp: map.remove("HugePages_Surp"),
387            hugepagesize: map.remove("Hugepagesize"),
388            direct_map_4k: map.remove("DirectMap4k"),
389            direct_map_4M: map.remove("DirectMap4M"),
390            direct_map_2M: map.remove("DirectMap2M"),
391            direct_map_1G: map.remove("DirectMap1G"),
392            k_reclaimable: map.remove("KReclaimable"),
393            per_cpu: map.remove("Percpu"),
394            hugetlb: map.remove("Hugetlb"),
395            file_pmd_mapped: map.remove("FilePmdMapped"),
396            file_huge_pages: map.remove("FileHugePages"),
397            z_swap: map.remove("Zswap"),
398            z_swapped: map.remove("Zswapped"),
399        };
400
401        if cfg!(test) {
402            assert!(map.is_empty(), "meminfo map is not empty: {:#?}", map);
403        }
404
405        Ok(meminfo)
406    }
407}