blob: aeaa9125864e8190e31d5a0a50717bcbf5fd22b6 [file] [log] [blame]
Suren Baghdasaryan642048d2021-06-15 12:28:24 -07001#!/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
80watermark_scale=`cat /proc/sys/vm/watermark_scale_factor`
81
82# convert extra_free_kbytes to pages
83page_size=$(getconf PAGESIZE)
84page_size_kb=$((page_size/1024))
85extra_free_pg=$((extra_free_kbytes/page_size_kb))
86
87managed=($(grep managed /proc/zoneinfo | awk '{print $2}'))
88length=${#managed[@]}
89min=($(grep "min" /proc/zoneinfo | awk '{print $2}'))
90
91# calculate vm_total_pages.
92# WARNING: if the final low watermark differs from the original, the source of
93# the error is likely vm_total_pages which is impossible to get exact from the
94# userspace. Grep for "Total pages" in the kernel logs to see the actual
95# vm_total_pages and plug it in the calculation to confirm the source of the
96# error. Error caused by this inaccuracy is normally within 1% range.
97vm_total_pages=0
98i=0
99while [ $i -lt $length ]
100do
101 vm_total_pages=$((vm_total_pages + managed[i]))
102 i=$((i+1))
103done
104
105# calculate watermark_scale_new for each zone and choose the max
106max_watermark_scale=0
107i=0
108while [ $i -lt $length ]
109do
110 # skip unmanaged zones
111 if [ ${managed[i]} -eq 0 ]
112 then
113 i=$((i+1))
114 continue
115 fi
116
117 base_margin=$((min[i] / 4))
118 calc_margin=$(echo "${managed[i]} * $watermark_scale / 10000" | bc)
119 # round the value by adding 0.5 and truncating the decimal part
120 watermark_delta=$(echo "x=($extra_free_pg / ($vm_total_pages / 10000) + 0.5); scale = 0; x/1" | bc -l)
121 if [ $calc_margin -gt $base_margin ]
122 then
123 watermark_scale_new=$(echo "$watermark_scale + $watermark_delta" | bc)
124 else
125 watermark_scale_new=$(echo "$base_margin / (${managed[i]} / 10000) + $watermark_delta" | bc)
126 fi
127
128 if [ $max_watermark_scale -lt $watermark_scale_new ]
129 then
130 max_watermark_scale=$watermark_scale_new
131 fi
132
133 i=$((i+1))
134done
135
136echo $max_watermark_scale > /proc/sys/vm/watermark_scale_factor