1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
#!/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
# record the original watermark_scale_factor value
watermark_scale=$(getprop "ro.kernel.watermark_scale_factor")
if [ -z "$watermark_scale" ]
then
watermark_scale=$(cat /proc/sys/vm/watermark_scale_factor)
setprop "ro.kernel.watermark_scale_factor" "$watermark_scale"
# On older distributions with no policies configured setprop may fail.
# If that happens, use the kernel default of 10.
if [ -z $(getprop "ro.kernel.watermark_scale_factor") ]
then
watermark_scale=10
fi
fi
# 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
|