init: Add extra_free_kbytes.sh script to adjust watermark_scale_factor
Android uses an out-of-tree /proc/sys/vm/extra_free_kbytes kernel knob to increase low watermark. A similar /proc/sys/vm/watermark_scale_factor knob supported in upstream kernel can be used for the same purpose. The script implements conversion from desired extra_free_kbytes value to watermark_scale_factor value and sets watermark_scale_factor according to the value for extra_free_kbytes provided as a parameter. Note that watermark_scale_factor affects high watermark differently from extra_free_kbytes. While extra_free_kbytes increases low and high watermarks by the same amount, watermark_scale_factor affects high watermark 2x times. Therefore it is expected that replacing the usage extra_free_kbytes with watermark_scale_factor will keep the same low watermark but high watermark will be higher than before. Usage: extra_free_kbytes.sh <extra_free_kbytes value> Bug: 109664768 Signed-off-by: Suren Baghdasaryan <surenb@google.com> Change-Id: Id8949be731da77be884cdeec3a765ea291dbc2f0
This commit is contained in:
parent
b33cfbfc4b
commit
642048d969
2 changed files with 142 additions and 0 deletions
|
@ -236,6 +236,7 @@ cc_binary {
|
|||
"init.rc",
|
||||
"ueventd.rc",
|
||||
"e2fsdroid",
|
||||
"extra_free_kbytes.sh",
|
||||
"make_f2fs",
|
||||
"mke2fs",
|
||||
"sload_f2fs",
|
||||
|
@ -552,3 +553,8 @@ cc_binary {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
sh_binary {
|
||||
name: "extra_free_kbytes.sh",
|
||||
src: "extra_free_kbytes.sh",
|
||||
}
|
||||
|
|
136
init/extra_free_kbytes.sh
Executable file
136
init/extra_free_kbytes.sh
Executable file
|
@ -0,0 +1,136 @@
|
|||
#!/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
|
Loading…
Reference in a new issue