xref: /aosp_15_r20/system/extras/checkpoint_gc/checkpoint_gc.sh (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1#!/system/bin/sh
2
3#
4# Copyright (C) 2019 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19# This script will run as an pre-checkpointing cleanup for mounting f2fs
20# with checkpoint=disable, so that the first mount after the reboot will
21# be faster. It is unnecessary to run if the device does not use userdata
22# checkpointing on F2FS.
23
24# TARGET_SLOT="${1}"
25STATUS_FD="${2}"
26
27SLEEP=5
28TIME=0
29MAX_TIME=1200
30
31# GC_URGENT_MID, will fall back to GC_URGENT_HIGH if unsupported
32GC_TYPE=3
33
34# If we fall back, start off with less impactful GC
35# To avoid long wait time, ramp up over time
36GC_SLEEP_MAX=150
37GC_SLEEP_MIN=50
38GC_SLEEP_STEP=5
39
40# We only need to run this if we're using f2fs
41if [ ! -f /dev/sys/fs/by-name/userdata/gc_urgent ]; then
42  exit 0
43fi
44
45# If we have sufficient free segments, it doesn't matter how much extra
46# space is unusable, since we only need to make it to boot complete to
47# get that space back
48MIN_FREE_SEGMENT=500
49
50# Ideally we want to track unusable, as it directly measures what we
51# care about. If it's not present, dirty_segments is the best proxy.
52if [ -f /dev/sys/fs/by-name/userdata/unusable ]; then
53  UNUSABLE=1
54  METRIC="unusable blocks"
55  THRESHOLD=25000
56  MAX_INCREASE=500
57  read START < /dev/sys/fs/by-name/userdata/unusable
58else
59  METRIC="dirty segments"
60  THRESHOLD=200
61  MAX_INCREASE=5
62  read START < /dev/sys/fs/by-name/userdata/dirty_segments
63fi
64
65log -pi -t checkpoint_gc Turning on GC for userdata
66
67read OLD_SLEEP < /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time || \
68  { log -pw -t checkpoint_gc Cannot read gc_urgent_sleep_time; exit 1; }
69GC_SLEEP=${GC_SLEEP_MAX}
70echo ${GC_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time || \
71  { log -pw -t checkpoint_gc Cannot set gc_urgent_sleep_time; exit 1; }
72
73
74echo ${GC_TYPE} > /dev/sys/fs/by-name/userdata/gc_urgent \
75  || { GC_TYPE=1; log -pi -t checkpoint_gc GC_URGENT_MID not supported, using GC_URGENT_HIGH; }
76
77if [ ${GC_TYPE} -eq 1 ]; then
78  echo ${GC_TYPE} > /dev/sys/fs/by-name/userdata/gc_urgent || \
79    { echo ${OLD_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time; \
80    log -pw -t checkpoint_gc Failed to set gc_urgent; exit 1; }
81else
82  # GC MID will wait for background I/O, so no need to start small
83  GC_SLEEP=${GC_SLEEP_MIN}
84fi
85
86
87CURRENT=${START}
88TODO=$((${START}-${THRESHOLD}))
89CUTOFF=$((${START} + ${MAX_INCREASE}))
90while [ ${CURRENT} -gt ${THRESHOLD} ]; do
91  log -pi -t checkpoint_gc ${METRIC}:${CURRENT} \(threshold:${THRESHOLD}\) mode:${GC_TYPE} GC_SLEEP:${GC_SLEEP}
92  PROGRESS=`echo "(${START}-${CURRENT})/${TODO}"|bc -l`
93  if [[ $PROGRESS == -* ]]; then
94      PROGRESS=0
95  fi
96  print -u${STATUS_FD} "global_progress ${PROGRESS}"
97  if [ ${UNUSABLE} -eq 1 ]; then
98    read CURRENT < /dev/sys/fs/by-name/userdata/unusable
99  else
100    read CURRENT < /dev/sys/fs/by-name/userdata/dirty_segments
101  fi
102
103  if [ ${CURRENT} -gt ${CUTOFF} ]; then
104    log -pw -t checkpoint_gc Garbage Collection is making no progress. Aborting checkpoint_gc attempt \(initial ${METRIC}: ${START}, now: ${CURRENT}\)
105    break
106  fi
107
108  read CURRENT_FREE_SEGMENTS < /dev/sys/fs/by-name/userdata/free_segments
109  read CURRENT_OVP < /dev/sys/fs/by-name/userdata/ovp_segments
110  CURRENT_FREE_SEG=$((${CURRENT_FREE_SEGMENTS}-${CURRENT_OVP}))
111  if [ ${CURRENT_FREE_SEG} -gt ${MIN_FREE_SEGMENT} ]; then
112    log -pi checkpoint_gc Sufficient free segments. Extra gc not needed.
113    break
114  fi
115
116  sleep ${SLEEP}
117  TIME=$((${TIME}+${SLEEP}))
118  if [ ${TIME} -gt ${MAX_TIME} ]; then
119    log -pw -t checkpoint_gc Timed out with gc threshold not met.
120    break
121  fi
122  if [ ${GC_SLEEP} -gt ${GC_SLEEP_MIN} ]; then
123    GC_SLEEP=$((${GC_SLEEP}-${GC_SLEEP_STEP}))
124  fi
125  # In case someone turns it off behind our back
126  echo ${GC_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time
127  echo ${GC_TYPE} > /dev/sys/fs/by-name/userdata/gc_urgent
128done
129
130# It could be a while before the system reboots for the update...
131# Leaving on low level GC can help ensure the boot for ota is faster
132# If powerhints decides to turn it off, we'll just rely on normal GC
133log -pi -t checkpoint_gc Leaving on GC_URGENT_LOW for userdata
134echo ${OLD_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time
135echo 2 > /dev/sys/fs/by-name/userdata/gc_urgent
136sync
137
138print -u${STATUS_FD} "global_progress 1.0"
139exit 0
140