Commit 9fcfda8f authored by opara's avatar opara 🙃
Browse files

tor-memquota: moved auto calc into separate function

parent 41be937d
Loading
Loading
Loading
Loading
+112 −49
Original line number Diff line number Diff line
@@ -169,18 +169,56 @@ impl ConfigBuilder {
        // but we have no way of knowing whether we are a relay or not here.
        let max = match max {
            ExplicitOrAuto::Explicit(x) => x,
            ExplicitOrAuto::Auto => 'auto: {
            ExplicitOrAuto::Auto => compute_max_from_total_system_mem(total_available_memory()),
        };

        let low_water = match low_water {
            ExplicitOrAuto::Explicit(x) => x,
            ExplicitOrAuto::Auto => Qty((*max as f32 * 0.75) as _),
        };

        let config = ConfigInner { max, low_water };

        /// Minimum low water.  `const` so that overflows are compile-time.
        const MIN_LOW_WATER: usize = crate::mtracker::MAX_CACHE.as_usize() * MIN_MAX_PARTICIPANTS;
        let min_low_water = MIN_LOW_WATER;
        if *config.low_water < min_low_water {
            return Err(ConfigBuildError::Invalid {
                field: "low_water".into(),
                problem: format!("must be at least {min_low_water}"),
            });
        }

        let ratio: f32 = *config.low_water as f32 / *config.max as f32;
        if ratio > MAX_LOW_WATER_RATIO {
            return Err(ConfigBuildError::Inconsistent {
                fields: vec!["low_water".into(), "max".into()],
                problem: format!(
 "low_water / max = {ratio}; must be <= {MAX_LOW_WATER_RATIO}, ideally considerably lower"
                ),
            });
        }

        Ok(Config(IfEnabled::Enabled(config, enabled)))
    }
}

/// Determine a max given the system's total available memory.
///
/// This is used when `max` is configured as "auto".
/// It takes a `Result` so that we can handle the case where the total memory isn't available.
fn compute_max_from_total_system_mem(mem: Result<usize, MemQueryError>) -> Qty {
    const MIB: usize = 1024 * 1024;
    const GIB: usize = 1024 * 1024 * 1024;

                let mem = match total_available_memory() {
    let mem = match mem {
        Ok(x) => x,
        Err(e) => {
            warn!("Unable to get the total available memory. Using a constant max instead: {e}");

            // Can't get the total available memory,
            // so we return a max depending on whether the architecture is 32-bit or 64-bit.
                        break 'auto Qty({
            return Qty({
                cfg_if::cfg_if! {
                    if #[cfg(target_pointer_width = "64")] {
                        8 * GIB
@@ -218,38 +256,6 @@ impl ConfigBuilder {

    Qty(mem)
}
        };

        let low_water = match low_water {
            ExplicitOrAuto::Explicit(x) => x,
            ExplicitOrAuto::Auto => Qty((*max as f32 * 0.75) as _),
        };

        let config = ConfigInner { max, low_water };

        /// Minimum low water.  `const` so that overflows are compile-time.
        const MIN_LOW_WATER: usize = crate::mtracker::MAX_CACHE.as_usize() * MIN_MAX_PARTICIPANTS;
        let min_low_water = MIN_LOW_WATER;
        if *config.low_water < min_low_water {
            return Err(ConfigBuildError::Invalid {
                field: "low_water".into(),
                problem: format!("must be at least {min_low_water}"),
            });
        }

        let ratio: f32 = *config.low_water as f32 / *config.max as f32;
        if ratio > MAX_LOW_WATER_RATIO {
            return Err(ConfigBuildError::Inconsistent {
                fields: vec!["low_water".into(), "max".into()],
                problem: format!(
 "low_water / max = {ratio}; must be <= {MAX_LOW_WATER_RATIO}, ideally considerably lower"
                ),
            });
        }

        Ok(Config(IfEnabled::Enabled(config, enabled)))
    }
}

/// The total available memory in bytes.
///
@@ -443,4 +449,61 @@ mod test {
            b.build().unwrap();
        }
    }

    /// Test the logic that computes the `max` when configured as "auto".
    #[test]
    // We do some `1 * X` operations below for readability.
    #[allow(clippy::identity_op)]
    fn auto_max() {
        #[allow(unused)]
        fn check_helper(val: Qty, expected_32: Qty, expected_64: Qty) {
            assert_eq!(val, {
                cfg_if::cfg_if! {
                    if #[cfg(target_pointer_width = "64")] {
                        expected_64
                    } else if #[cfg(target_pointer_width = "32")] {
                        expected_32
                    } else {
                        panic!("Unsupported architecture :(");
                    }
                }
            });
        }

        check_helper(
            compute_max_from_total_system_mem(Err(MemQueryError::Unavailable)),
            /* 32-bit */ Qty(1 * 1024 * 1024 * 1024),
            /* 64-bit */ Qty(8 * 1024 * 1024 * 1024),
        );
        check_helper(
            compute_max_from_total_system_mem(Ok(8 * 1024 * 1024 * 1024)),
            /* 32-bit */ Qty(2 * 1024 * 1024 * 1024),
            /* 64-bit */ Qty(3435973836),
        );
        check_helper(
            compute_max_from_total_system_mem(Ok(7 * 1024 * 1024 * 1024)),
            /* 32-bit */ Qty(2 * 1024 * 1024 * 1024),
            /* 64-bit */ Qty(5637144576),
        );
        check_helper(
            compute_max_from_total_system_mem(Ok(1 * 1024 * 1024 * 1024)),
            /* 32-bit */ Qty(805306368),
            /* 64-bit */ Qty(805306368),
        );
        check_helper(
            compute_max_from_total_system_mem(Ok(7 * 1024)),
            /* 32-bit */ Qty(256 * 1024 * 1024),
            /* 64-bit */ Qty(256 * 1024 * 1024),
        );
        check_helper(
            compute_max_from_total_system_mem(Ok(0)),
            /* 32-bit */ Qty(256 * 1024 * 1024),
            /* 64-bit */ Qty(256 * 1024 * 1024),
        );
        check_helper(
            compute_max_from_total_system_mem(Ok(usize::MAX)),
            /* 32-bit */ Qty(2 * 1024 * 1024 * 1024),
            /* 64-bit */ Qty(8 * 1024 * 1024 * 1024),
        );
    }
}