1#!/bin/sh 2 3# Script implements watermark_scale calculation which results in the same low 4# watermark as if extra_free_kbytes tunable were to be used. 5# 6# Usage: extra_free_kbytes.sh <extra_free_kbytes value> 7# 8# extra_free_kbytes is distributed between zones based on 9# zone.managed_pages/vm_total_pages ratio, where vm_total_pages is the sum of 10# zone.managed_pages for all zones (zone.high used in this calculation is 0 11# when this is calculated). Therefore for each zone its share is calculated as: 12# 13# extra_free_pages = extra_free_kbytes / page_size 14# extra_share = extra_free_pages * managed_pages / vm_total_pages 15# 16# This extra_share is added to the low and high watermarks: 17# 18# low = min + max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share 19# high = min + 2 * max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share 20# 21# Because Android uses extra_free_kbytes to adjust the low watermark, we ignore 22# the difference in how watermark_scale and extra_free_kbytes affect the high 23# watermark and will match the low watermark only. 24# 25# To eliminate extra_share and compansate the difference with watermark_scale, 26# a new watermark_scale_new is calculated as: 27# 28# (1) max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share = 29# max(min / 4, managed_pages * (watermark_scale_new / 10000)) 30# 31# Two cases to consider: 32# A. managed_pages * (watermark_scale / 10000) > min / 4 33# The formula (1) becomes: 34# 35# managed_pages * (watermark_scale / 10000) + extra_share = 36# managed_pages * (watermark_scale_new / 10000) 37# 38# after simplifying and substituting extra_share formula becomes: 39# 40# (2) watermark_scale_new = watermark_scale + extra_free_pages / vm_total_pages * 10000 41# 42# B. managed_pages * (watermark_scale / 10000) < min / 4 43# The formula (1) becomes: 44# 45# min / 4 + extra_share = max(min / 4, managed_pages * (watermark_scale_new / 10000)) 46# 47# after calculating watermark_scale_new, if (managed_pages * (watermark_scale_new / 10000)) 48# is still smaller than min / 4 then we can't compensate extra_share with 49# watermark_scale anyway. Therefore calculation becomes: 50# 51# watermark_scale_new = (min / 4 + extra_share) / managed_pages * 10000 52# 53# after simplifying and substituting extra_share formula becomes: 54# 55# (3) watermark_scale_new = (min / 4) * 10000 / managed_pages + extra_free_pages / vm_total_pages * 10000 56# 57# After defining watermark_delta = extra_free_pages / vm_total_pages * 10000: 58# 59# if (managed_pages * (watermark_scale / 10000) > min / 4) 60# watermark_scale_new = watermark_scale + watermark_delta 61# else 62# watermark_scale_new = (min / 4) * 10000 / managed_pages + watermark_delta 63# 64 65if [ "$#" -ne 1 ] 66then 67 echo "Usage: $0 <extra_free_kbytes value>" 68 exit 69fi 70 71extra_free_kbytes=$1 72 73# if extra_free_kbytes knob exists, use it and exit 74if [ -e /proc/sys/vm/extra_free_kbytes ] 75then 76 echo $extra_free_kbytes > /proc/sys/vm/extra_free_kbytes 77 exit 78fi 79 80# record the original watermark_scale_factor value 81watermark_scale=$(getprop "ro.kernel.watermark_scale_factor") 82if [ -z "$watermark_scale" ] 83then 84 watermark_scale=$(cat /proc/sys/vm/watermark_scale_factor) 85 setprop "ro.kernel.watermark_scale_factor" "$watermark_scale" 86 # On older distributions with no policies configured setprop may fail. 87 # If that happens, use the kernel default of 10. 88 if [ -z $(getprop "ro.kernel.watermark_scale_factor") ] 89 then 90 watermark_scale=10 91 fi 92fi 93 94# convert extra_free_kbytes to pages 95page_size=$(getconf PAGESIZE) 96page_size_kb=$((page_size/1024)) 97extra_free_pg=$((extra_free_kbytes/page_size_kb)) 98 99managed=($(grep managed /proc/zoneinfo | awk '{print $2}')) 100length=${#managed[@]} 101min=($(grep "min" /proc/zoneinfo | awk '{print $2}')) 102 103# calculate vm_total_pages. 104# WARNING: if the final low watermark differs from the original, the source of 105# the error is likely vm_total_pages which is impossible to get exact from the 106# userspace. Grep for "Total pages" in the kernel logs to see the actual 107# vm_total_pages and plug it in the calculation to confirm the source of the 108# error. Error caused by this inaccuracy is normally within 1% range. 109vm_total_pages=0 110i=0 111while [ $i -lt $length ] 112do 113 vm_total_pages=$((vm_total_pages + managed[i])) 114 i=$((i+1)) 115done 116 117# calculate watermark_scale_new for each zone and choose the max 118max_watermark_scale=0 119i=0 120while [ $i -lt $length ] 121do 122 # skip unmanaged zones 123 if [ ${managed[i]} -eq 0 ] 124 then 125 i=$((i+1)) 126 continue 127 fi 128 129 base_margin=$((min[i] / 4)) 130 calc_margin=$(echo "${managed[i]} * $watermark_scale / 10000" | bc) 131 # round the value by adding 0.5 and truncating the decimal part 132 watermark_delta=$(echo "x=($extra_free_pg / ($vm_total_pages / 10000) + 0.5); scale = 0; x/1" | bc -l) 133 if [ $calc_margin -gt $base_margin ] 134 then 135 watermark_scale_new=$(echo "$watermark_scale + $watermark_delta" | bc) 136 else 137 watermark_scale_new=$(echo "$base_margin / (${managed[i]} / 10000) + $watermark_delta" | bc) 138 fi 139 140 if [ $max_watermark_scale -lt $watermark_scale_new ] 141 then 142 max_watermark_scale=$watermark_scale_new 143 fi 144 145 i=$((i+1)) 146done 147 148echo $max_watermark_scale > /proc/sys/vm/watermark_scale_factor 149