blob: 3a4aff2ea9aa189c6ba7c6ae7bf87684b5509b7b [file] [log] [blame]
Srinivas Paladuguc8261752019-01-09 10:54:52 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.job.JobInfo;
20import android.app.job.JobParameters;
21import android.app.job.JobScheduler;
22import android.app.job.JobService;
23import android.content.ComponentName;
24import android.content.Context;
25import android.os.FileUtils;
26import android.os.SystemProperties;
27import android.util.Slog;
28
29import java.io.File;
30import java.io.IOException;
31import java.util.concurrent.TimeUnit;
32
33/**
34 * Schedules jobs for triggering zram writeback.
35 */
36public final class ZramWriteback extends JobService {
37 private static final String TAG = "ZramWriteback";
38 private static final boolean DEBUG = false;
39
40 private static final ComponentName sZramWriteback =
41 new ComponentName("android", ZramWriteback.class.getName());
42
43 private static final int MARK_IDLE_JOB_ID = 811;
44 private static final int WRITEBACK_IDLE_JOB_ID = 812;
45
46 private static final int MAX_ZRAM_DEVICES = 256;
47 private static int sZramDeviceId = 0;
48
49 private static final String IDLE_SYS = "/sys/block/zram%d/idle";
50 private static final String IDLE_SYS_ALL_PAGES = "all";
51
52 private static final String WB_SYS = "/sys/block/zram%d/writeback";
53 private static final String WB_SYS_IDLE_PAGES = "idle";
54
55 private static final String WB_STATS_SYS = "/sys/block/zram%d/bd_stat";
56 private static final int WB_STATS_MAX_FILE_SIZE = 128;
57
58 private static final String BDEV_SYS = "/sys/block/zram%d/backing_dev";
59
60 private static final String MARK_IDLE_DELAY_PROP = "ro.zram.mark_idle_delay_mins";
61 private static final String FIRST_WB_DELAY_PROP = "ro.zram.first_wb_delay_mins";
62 private static final String PERIODIC_WB_DELAY_PROP = "ro.zram.periodic_wb_delay_hours";
63
64 private void markPagesAsIdle() {
65 String idlePath = String.format(IDLE_SYS, sZramDeviceId);
66 try {
67 FileUtils.stringToFile(new File(idlePath), IDLE_SYS_ALL_PAGES);
68 } catch (IOException e) {
69 Slog.e(TAG, "Failed to write to " + idlePath);
70 }
71 }
72
73 private void flushIdlePages() {
74 if (DEBUG) Slog.d(TAG, "Start writing back idle pages to disk");
75 String wbPath = String.format(WB_SYS, sZramDeviceId);
76 try {
77 FileUtils.stringToFile(new File(wbPath), WB_SYS_IDLE_PAGES);
78 } catch (IOException e) {
79 Slog.e(TAG, "Failed to write to " + wbPath);
80 }
81 if (DEBUG) Slog.d(TAG, "Finished writeback back idle pages");
82 }
83
84 private int getWrittenPageCount() {
85 String wbStatsPath = String.format(WB_STATS_SYS, sZramDeviceId);
86 try {
87 String wbStats = FileUtils
88 .readTextFile(new File(wbStatsPath), WB_STATS_MAX_FILE_SIZE, "");
89 return Integer.parseInt(wbStats.trim().split("\\s+")[2], 10);
90 } catch (IOException e) {
91 Slog.e(TAG, "Failed to read writeback stats from " + wbStatsPath);
92 }
93
94 return -1;
95 }
96
97 private void markAndFlushPages() {
98 int pageCount = getWrittenPageCount();
99
100 flushIdlePages();
101 markPagesAsIdle();
102
103 if (pageCount != -1) {
104 Slog.i(TAG, "Total pages written to disk is " + (getWrittenPageCount() - pageCount));
105 }
106 }
107
108 private static boolean isWritebackEnabled() {
109 try {
110 String backingDev = FileUtils
111 .readTextFile(new File(String.format(BDEV_SYS, sZramDeviceId)), 128, "");
112 if (!"none".equals(backingDev.trim())) {
113 return true;
114 } else {
115 Slog.w(TAG, "Writeback device is not set");
116 }
117 } catch (IOException e) {
118 Slog.w(TAG, "Writeback is not enabled on zram");
119 }
120 return false;
121 }
122
123 private static void schedNextWriteback(Context context) {
124 int nextWbDelay = SystemProperties.getInt(PERIODIC_WB_DELAY_PROP, 24);
125 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
126
127 js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
128 .setMinimumLatency(TimeUnit.HOURS.toMillis(nextWbDelay))
129 .setRequiresDeviceIdle(true)
130 .build());
131 }
132
133 @Override
134 public boolean onStartJob(JobParameters params) {
135
136 if (!isWritebackEnabled()) {
137 jobFinished(params, false);
138 return false;
139 }
140
141 if (params.getJobId() == MARK_IDLE_JOB_ID) {
142 markPagesAsIdle();
143 jobFinished(params, false);
144 return false;
145 } else {
146 new Thread("ZramWriteback_WritebackIdlePages") {
147 @Override
148 public void run() {
149 markAndFlushPages();
150 schedNextWriteback(ZramWriteback.this);
151 jobFinished(params, false);
152 }
153 }.start();
154 }
155 return true;
156 }
157
158 @Override
159 public boolean onStopJob(JobParameters params) {
160 // The thread that triggers the writeback is non-interruptible
161 return false;
162 }
163
164 /**
165 * Schedule the zram writeback job to trigger a writeback when idle
166 */
167 public static void scheduleZramWriteback(Context context) {
168 int markIdleDelay = SystemProperties.getInt(MARK_IDLE_DELAY_PROP, 20);
169 int firstWbDelay = SystemProperties.getInt(FIRST_WB_DELAY_PROP, 180);
170
171 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
172
173 // Schedule a one time job to mark pages as idle. These pages will be written
174 // back at later point if they remain untouched.
175 js.schedule(new JobInfo.Builder(MARK_IDLE_JOB_ID, sZramWriteback)
176 .setMinimumLatency(TimeUnit.MINUTES.toMillis(markIdleDelay))
177 .build());
178
179 // Schedule a one time job to flush idle pages to disk.
180 // After the initial writeback, subsequent writebacks are done at interval set
181 // by ro.zram.periodic_wb_delay_hours.
182 js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
183 .setMinimumLatency(TimeUnit.MINUTES.toMillis(firstWbDelay))
184 .setRequiresDeviceIdle(true)
185 .build());
186 }
187}