#!/system/bin/sh # # Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script will run as an pre-checkpointing cleanup for mounting f2fs # with checkpoint=disable, so that the first mount after the reboot will # be faster. It is unnecessary to run if the device does not use userdata # checkpointing on F2FS. # TARGET_SLOT="${1}" STATUS_FD="${2}" SLEEP=5 TIME=0 MAX_TIME=1200 # GC_URGENT_MID, will fall back to GC_URGENT_HIGH if unsupported GC_TYPE=3 # If we fall back, start off with less impactful GC # To avoid long wait time, ramp up over time GC_SLEEP_MAX=150 GC_SLEEP_MIN=50 GC_SLEEP_STEP=5 # We only need to run this if we're using f2fs if [ ! -f /dev/sys/fs/by-name/userdata/gc_urgent ]; then exit 0 fi # If we have sufficient free segments, it doesn't matter how much extra # space is unusable, since we only need to make it to boot complete to # get that space back MIN_FREE_SEGMENT=500 # Ideally we want to track unusable, as it directly measures what we # care about. If it's not present, dirty_segments is the best proxy. if [ -f /dev/sys/fs/by-name/userdata/unusable ]; then UNUSABLE=1 METRIC="unusable blocks" THRESHOLD=25000 MAX_INCREASE=500 read START < /dev/sys/fs/by-name/userdata/unusable else METRIC="dirty segments" THRESHOLD=200 MAX_INCREASE=5 read START < /dev/sys/fs/by-name/userdata/dirty_segments fi log -pi -t checkpoint_gc Turning on GC for userdata read OLD_SLEEP < /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time || \ { log -pw -t checkpoint_gc Cannot read gc_urgent_sleep_time; exit 1; } GC_SLEEP=${GC_SLEEP_MAX} echo ${GC_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time || \ { log -pw -t checkpoint_gc Cannot set gc_urgent_sleep_time; exit 1; } echo ${GC_TYPE} > /dev/sys/fs/by-name/userdata/gc_urgent \ || { GC_TYPE=1; log -pi -t checkpoint_gc GC_URGENT_MID not supported, using GC_URGENT_HIGH; } if [ ${GC_TYPE} -eq 1 ]; then echo ${GC_TYPE} > /dev/sys/fs/by-name/userdata/gc_urgent || \ { echo ${OLD_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time; \ log -pw -t checkpoint_gc Failed to set gc_urgent; exit 1; } else # GC MID will wait for background I/O, so no need to start small GC_SLEEP=${GC_SLEEP_MIN} fi CURRENT=${START} TODO=$((${START}-${THRESHOLD})) CUTOFF=$((${START} + ${MAX_INCREASE})) while [ ${CURRENT} -gt ${THRESHOLD} ]; do log -pi -t checkpoint_gc ${METRIC}:${CURRENT} \(threshold:${THRESHOLD}\) mode:${GC_TYPE} GC_SLEEP:${GC_SLEEP} PROGRESS=`echo "(${START}-${CURRENT})/${TODO}"|bc -l` if [[ $PROGRESS == -* ]]; then PROGRESS=0 fi print -u${STATUS_FD} "global_progress ${PROGRESS}" if [ ${UNUSABLE} -eq 1 ]; then read CURRENT < /dev/sys/fs/by-name/userdata/unusable else read CURRENT < /dev/sys/fs/by-name/userdata/dirty_segments fi if [ ${CURRENT} -gt ${CUTOFF} ]; then log -pw -t checkpoint_gc Garbage Collection is making no progress. Aborting checkpoint_gc attempt \(initial ${METRIC}: ${START}, now: ${CURRENT}\) break fi read CURRENT_FREE_SEGMENTS < /dev/sys/fs/by-name/userdata/free_segments read CURRENT_OVP < /dev/sys/fs/by-name/userdata/ovp_segments CURRENT_FREE_SEG=$((${CURRENT_FREE_SEGMENTS}-${CURRENT_OVP})) if [ ${CURRENT_FREE_SEG} -gt ${MIN_FREE_SEGMENT} ]; then log -pi checkpoint_gc Sufficient free segments. Extra gc not needed. break fi sleep ${SLEEP} TIME=$((${TIME}+${SLEEP})) if [ ${TIME} -gt ${MAX_TIME} ]; then log -pw -t checkpoint_gc Timed out with gc threshold not met. break fi if [ ${GC_SLEEP} -gt ${GC_SLEEP_MIN} ]; then GC_SLEEP=$((${GC_SLEEP}-${GC_SLEEP_STEP})) fi # In case someone turns it off behind our back echo ${GC_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time echo ${GC_TYPE} > /dev/sys/fs/by-name/userdata/gc_urgent done # It could be a while before the system reboots for the update... # Leaving on low level GC can help ensure the boot for ota is faster # If powerhints decides to turn it off, we'll just rely on normal GC log -pi -t checkpoint_gc Leaving on GC_URGENT_LOW for userdata echo ${OLD_SLEEP} > /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time echo 2 > /dev/sys/fs/by-name/userdata/gc_urgent sync print -u${STATUS_FD} "global_progress 1.0" exit 0