From b38da4a8bf8345d207c208044c3b83e65274d093 Mon Sep 17 00:00:00 2001 From: Lasse Collin Date: Wed, 28 Feb 2024 19:55:48 +0200 Subject: [PATCH] xz: Read MemAvailable from /proc/meminfo on Linux. This uses 75 % of MemAvailable as the default soft memory usage limit for -T0. It can be checked with --info-memory. The use of MemAvailable was first suggested by Sebastian Andrzej Siewior in 2021. This is a different implementation, including the 75 % part. --- src/xz/hardware.c | 58 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/xz/hardware.c b/src/xz/hardware.c index 952652fe..79c267ac 100644 --- a/src/xz/hardware.c +++ b/src/xz/hardware.c @@ -307,19 +307,69 @@ hardware_memlimit_show(void) } +#ifdef __linux__ +static uint64_t +get_available_ram(void) +{ + FILE *f = fopen("/proc/meminfo", "r"); + if (f == NULL) + return 0; + + // The information is near the very beginning of the file. + char buf[256]; + + // Read one byte less than sizeof(buf) to leave room for '\0'. + const size_t size = fread(buf, 1, sizeof(buf) - 1, f); + (void)fclose(f); + + buf[size] = '\0'; + + const char needle[] = "\nMemAvailable:"; + char *str = strstr(buf, needle); + if (str == NULL) + return 0; + + // Skip the needle. + str += sizeof(needle) - 1; + + // Skip spaces. + while (*str == ' ') + ++str; + + // Here it seems safe enough to assume that there won't be + // an integer overflow or at least it won't cause big trouble. + uint64_t ret = 0; + while (*str >= '0' && *str <= '9') + ret = ret * 10 + (uint64_t)(*str++ - '0'); + + // The value should end with " kB" so check for the space. + if (*str != ' ') + return 0; + + return 1024 * ret; +} +#endif + + extern void hardware_init(void) { + uint64_t available_ram = 0; +#ifdef __linux__ + available_ram = get_available_ram(); +#endif + // Get the amount of RAM. If we cannot determine it, // use the assumption defined by the configure script. total_ram = lzma_physmem(); if (total_ram == 0) total_ram = (uint64_t)(ASSUME_RAM) * 1024 * 1024; - // FIXME? There may be better methods to determine the default value. - // One Linux-specific suggestion is to use MemAvailable from - // /proc/meminfo as the starting point. - memlimit_mt_default = total_ram / 4; + // Use 75 % of currently-available RAM or 25 % of total RAM + // as the default value. + memlimit_mt_default = available_ram != 0 + ? available_ram - available_ram / 4 + : total_ram / 4; #if SIZE_MAX == UINT32_MAX // A too high value may cause 32-bit xz to run out of address space.