blob: 5d97def129a48e657dbb5071bbbe35fe0de148f0 [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";
Srinivas Paladugu23d6f0a2019-10-22 10:47:05 -070063 private static final String FORCE_WRITEBACK_PROP = "zram.force_writeback";
Srinivas Paladuguc8261752019-01-09 10:54:52 -080064
65 private void markPagesAsIdle() {
66 String idlePath = String.format(IDLE_SYS, sZramDeviceId);
67 try {
68 FileUtils.stringToFile(new File(idlePath), IDLE_SYS_ALL_PAGES);
69 } catch (IOException e) {
70 Slog.e(TAG, "Failed to write to " + idlePath);
71 }
72 }
73
74 private void flushIdlePages() {
75 if (DEBUG) Slog.d(TAG, "Start writing back idle pages to disk");
76 String wbPath = String.format(WB_SYS, sZramDeviceId);
77 try {
78 FileUtils.stringToFile(new File(wbPath), WB_SYS_IDLE_PAGES);
79 } catch (IOException e) {
80 Slog.e(TAG, "Failed to write to " + wbPath);
81 }
82 if (DEBUG) Slog.d(TAG, "Finished writeback back idle pages");
83 }
84
85 private int getWrittenPageCount() {
86 String wbStatsPath = String.format(WB_STATS_SYS, sZramDeviceId);
87 try {
88 String wbStats = FileUtils
89 .readTextFile(new File(wbStatsPath), WB_STATS_MAX_FILE_SIZE, "");
90 return Integer.parseInt(wbStats.trim().split("\\s+")[2], 10);
91 } catch (IOException e) {
92 Slog.e(TAG, "Failed to read writeback stats from " + wbStatsPath);
93 }
94
95 return -1;
96 }
97
98 private void markAndFlushPages() {
99 int pageCount = getWrittenPageCount();
100
101 flushIdlePages();
102 markPagesAsIdle();
103
104 if (pageCount != -1) {
105 Slog.i(TAG, "Total pages written to disk is " + (getWrittenPageCount() - pageCount));
106 }
107 }
108
109 private static boolean isWritebackEnabled() {
110 try {
111 String backingDev = FileUtils
112 .readTextFile(new File(String.format(BDEV_SYS, sZramDeviceId)), 128, "");
113 if (!"none".equals(backingDev.trim())) {
114 return true;
115 } else {
116 Slog.w(TAG, "Writeback device is not set");
117 }
118 } catch (IOException e) {
119 Slog.w(TAG, "Writeback is not enabled on zram");
120 }
121 return false;
122 }
123
124 private static void schedNextWriteback(Context context) {
125 int nextWbDelay = SystemProperties.getInt(PERIODIC_WB_DELAY_PROP, 24);
Srinivas Paladugu23d6f0a2019-10-22 10:47:05 -0700126 boolean forceWb = SystemProperties.getBoolean(FORCE_WRITEBACK_PROP, false);
Srinivas Paladuguc8261752019-01-09 10:54:52 -0800127 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
128
129 js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
130 .setMinimumLatency(TimeUnit.HOURS.toMillis(nextWbDelay))
Srinivas Paladugu23d6f0a2019-10-22 10:47:05 -0700131 .setRequiresDeviceIdle(!forceWb)
Srinivas Paladuguc8261752019-01-09 10:54:52 -0800132 .build());
133 }
134
135 @Override
136 public boolean onStartJob(JobParameters params) {
137
138 if (!isWritebackEnabled()) {
139 jobFinished(params, false);
140 return false;
141 }
142
143 if (params.getJobId() == MARK_IDLE_JOB_ID) {
144 markPagesAsIdle();
145 jobFinished(params, false);
146 return false;
147 } else {
148 new Thread("ZramWriteback_WritebackIdlePages") {
149 @Override
150 public void run() {
151 markAndFlushPages();
152 schedNextWriteback(ZramWriteback.this);
153 jobFinished(params, false);
154 }
155 }.start();
156 }
157 return true;
158 }
159
160 @Override
161 public boolean onStopJob(JobParameters params) {
162 // The thread that triggers the writeback is non-interruptible
163 return false;
164 }
165
166 /**
167 * Schedule the zram writeback job to trigger a writeback when idle
168 */
169 public static void scheduleZramWriteback(Context context) {
170 int markIdleDelay = SystemProperties.getInt(MARK_IDLE_DELAY_PROP, 20);
171 int firstWbDelay = SystemProperties.getInt(FIRST_WB_DELAY_PROP, 180);
Srinivas Paladugu23d6f0a2019-10-22 10:47:05 -0700172 boolean forceWb = SystemProperties.getBoolean(FORCE_WRITEBACK_PROP, false);
Srinivas Paladuguc8261752019-01-09 10:54:52 -0800173
174 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
175
176 // Schedule a one time job to mark pages as idle. These pages will be written
177 // back at later point if they remain untouched.
178 js.schedule(new JobInfo.Builder(MARK_IDLE_JOB_ID, sZramWriteback)
179 .setMinimumLatency(TimeUnit.MINUTES.toMillis(markIdleDelay))
Wei Wangf36b4902019-02-20 16:45:34 -0800180 .setOverrideDeadline(TimeUnit.MINUTES.toMillis(markIdleDelay))
Srinivas Paladuguc8261752019-01-09 10:54:52 -0800181 .build());
182
183 // Schedule a one time job to flush idle pages to disk.
184 // After the initial writeback, subsequent writebacks are done at interval set
185 // by ro.zram.periodic_wb_delay_hours.
186 js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
187 .setMinimumLatency(TimeUnit.MINUTES.toMillis(firstWbDelay))
Srinivas Paladugu23d6f0a2019-10-22 10:47:05 -0700188 .setRequiresDeviceIdle(!forceWb)
Srinivas Paladuguc8261752019-01-09 10:54:52 -0800189 .build());
190 }
191}