Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7cd5b81 authored by Suren Baghdasaryan's avatar Suren Baghdasaryan Committed by Automerger Merge Worker
Browse files

init: Add extra_free_kbytes.sh script to adjust watermark_scale_factor am:...

init: Add extra_free_kbytes.sh script to adjust watermark_scale_factor am: 642048d9 am: 5cdaecf6

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1780972

Change-Id: I6330280a8d4c6d96ce37c3823e65ed71295beafc
parents 0c97e375 5cdaecf6
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -236,6 +236,7 @@ cc_binary {
                "init.rc",
                "ueventd.rc",
                "e2fsdroid",
                "extra_free_kbytes.sh",
                "make_f2fs",
                "mke2fs",
                "sload_f2fs",
@@ -554,3 +555,8 @@ cc_binary {
        },
    },
}

sh_binary {
    name: "extra_free_kbytes.sh",
    src: "extra_free_kbytes.sh",
}
+136 −0
Original line number Diff line number Diff line
#!/bin/sh

# Script implements watermark_scale calculation which results in the same low
# watermark as if extra_free_kbytes tunable were to be used.
#
# Usage: extra_free_kbytes.sh <extra_free_kbytes value>
#
# extra_free_kbytes is distributed between zones based on
# zone.managed_pages/vm_total_pages ratio, where vm_total_pages is the sum of
# zone.managed_pages for all zones (zone.high used in this calculation is 0
# when this is calculated). Therefore for each zone its share is calculated as:
#
# extra_free_pages = extra_free_kbytes / page_size
# extra_share = extra_free_pages * managed_pages / vm_total_pages
#
# This extra_share is added to the low and high watermarks:
#
# low = min + max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
# high = min + 2 * max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share
#
# Because Android uses extra_free_kbytes to adjust the low watermark, we ignore
# the difference in how watermark_scale and extra_free_kbytes affect the high
# watermark and will match the low watermark only.
#
# To eliminate extra_share and compansate the difference with watermark_scale,
# a new watermark_scale_new is calculated as:
#
# (1) max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share =
#   max(min / 4, managed_pages * (watermark_scale_new / 10000))
#
# Two cases to consider:
# A. managed_pages * (watermark_scale / 10000) > min / 4
# The formula (1) becomes:
#
# managed_pages * (watermark_scale / 10000) + extra_share =
#   managed_pages * (watermark_scale_new / 10000)
#
# after simplifying and substituting extra_share formula becomes:
#
# (2) watermark_scale_new = watermark_scale + extra_free_pages / vm_total_pages * 10000
#
# B. managed_pages * (watermark_scale / 10000) < min / 4
# The formula (1) becomes:
#
# min / 4 + extra_share = max(min / 4, managed_pages * (watermark_scale_new / 10000))
#
# after calculating watermark_scale_new, if (managed_pages * (watermark_scale_new / 10000))
# is still smaller than min / 4 then we can't compensate extra_share with
# watermark_scale anyway. Therefore calculation becomes:
#
# watermark_scale_new = (min / 4 + extra_share) / managed_pages * 10000
#
# after simplifying and substituting extra_share formula becomes:
#
# (3) watermark_scale_new = (min / 4) * 10000 / managed_pages + extra_free_pages / vm_total_pages * 10000
#
# After defining watermark_delta = extra_free_pages / vm_total_pages * 10000:
#
# if (managed_pages * (watermark_scale / 10000) > min / 4)
#     watermark_scale_new = watermark_scale + watermark_delta
# else
#     watermark_scale_new = (min / 4) * 10000 / managed_pages + watermark_delta
#

if [ "$#" -ne 1 ]
then
    echo "Usage: $0 <extra_free_kbytes value>"
    exit
fi

extra_free_kbytes=$1

# if extra_free_kbytes knob exists, use it and exit
if [ -e /proc/sys/vm/extra_free_kbytes ]
then
    echo $extra_free_kbytes > /proc/sys/vm/extra_free_kbytes
    exit
fi

watermark_scale=`cat /proc/sys/vm/watermark_scale_factor`

# convert extra_free_kbytes to pages
page_size=$(getconf PAGESIZE)
page_size_kb=$((page_size/1024))
extra_free_pg=$((extra_free_kbytes/page_size_kb))

managed=($(grep managed /proc/zoneinfo | awk '{print $2}'))
length=${#managed[@]}
min=($(grep "min" /proc/zoneinfo | awk '{print $2}'))

# calculate vm_total_pages.
# WARNING: if the final low watermark differs from the original, the source of
# the error is likely vm_total_pages which is impossible to get exact from the
# userspace. Grep for "Total pages" in the kernel logs to see the actual
# vm_total_pages and plug it in the calculation to confirm the source of the
# error. Error caused by this inaccuracy is normally within 1% range.
vm_total_pages=0
i=0
while [ $i -lt $length ]
do
    vm_total_pages=$((vm_total_pages + managed[i]))
    i=$((i+1))
done

# calculate watermark_scale_new for each zone and choose the max
max_watermark_scale=0
i=0
while [ $i -lt $length ]
do
    # skip unmanaged zones
    if [ ${managed[i]} -eq 0 ]
    then
        i=$((i+1))
        continue
    fi

    base_margin=$((min[i] / 4))
    calc_margin=$(echo "${managed[i]} * $watermark_scale / 10000" | bc)
    # round the value by adding 0.5 and truncating the decimal part
    watermark_delta=$(echo "x=($extra_free_pg / ($vm_total_pages / 10000) + 0.5); scale = 0; x/1" | bc -l)
    if [ $calc_margin -gt $base_margin ]
    then
        watermark_scale_new=$(echo "$watermark_scale + $watermark_delta" | bc)
    else
        watermark_scale_new=$(echo "$base_margin / (${managed[i]} / 10000) + $watermark_delta" | bc)
    fi

    if [ $max_watermark_scale -lt $watermark_scale_new ]
    then
        max_watermark_scale=$watermark_scale_new
    fi

    i=$((i+1))
done

echo $max_watermark_scale > /proc/sys/vm/watermark_scale_factor