summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Martijn Coenen <maco@google.com> 2023-10-13 12:36:08 +0000
committer Martijn Coenen <maco@google.com> 2023-10-24 07:21:31 +0000
commit33fdb78a8e93b26279743d0ee2232d32ec37e97d (patch)
treef24414d0c334081c7922416e9ff62e13f291a80a
parent67890dbee5f9d2cab296fd2037fc69034b6a5d50 (diff)
BlobStore: use a separate thread for RevocableFd in system_server.
Having the callbacks on RevocableFileDescriptor coming in on the main thread of system_server can create problems: - system_server's main thread is heavily contended - it can cause deadlocks: callbacks come in from vold with vold's global lock held; this callback needs the system_server main thread to make progress. But if the main thread is busy with another call into vold (unrelated to RevocableFd), this will result in deadlock. Bug: 300351508 Test: atest BlobStoreManagerTest Change-Id: Ie4c3c65bdb9303f4aaab8f76b95d3f9f133b4c3e
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java2
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java3
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java25
-rw-r--r--core/java/android/os/RevocableFileDescriptor.java19
4 files changed, 45 insertions, 4 deletions
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 849432653bb3..4a0d57f55086 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -536,7 +536,7 @@ class BlobMetadata {
private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
String callingPackage, int callingUid) throws IOException {
final RevocableFileDescriptor revocableFd =
- new RevocableFileDescriptor(mContext, fd);
+ new RevocableFileDescriptor(mContext, fd, BlobStoreUtils.getRevocableFdHandler());
final Accessor accessor;
synchronized (mRevocableFds) {
accessor = new Accessor(callingPackage, callingUid);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 8eef8cebec3f..ede29ec168c0 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -223,7 +223,8 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
FileDescriptor fd = null;
try {
fd = openWriteInternal(offsetBytes, lengthBytes);
- final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
+ final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd,
+ BlobStoreUtils.getRevocableFdHandler());
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
IoUtils.closeQuietly(fd);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index 8f9427303dff..a4eae014bacd 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -24,6 +24,8 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.UserHandle;
import android.text.format.TimeMigrationUtils;
import android.util.Slog;
@@ -63,4 +65,27 @@ class BlobStoreUtils {
static String formatTime(long timeMs) {
return TimeMigrationUtils.formatMillisWithFixedFormat(timeMs);
}
+
+ private static Handler sRevocableFdHandler;
+ private static final Object sLock = new Object();
+
+ // By default, when using a RevocableFileDescriptor, callbacks will be sent to the process'
+ // main looper. In this case that would be system_server's main looper, which is a heavily
+ // contended thread. It can also cause deadlocks, because the volume daemon 'vold' holds a lock
+ // while making these callbacks to the system_server, while at the same time the system_server
+ // main thread can make a call into vold, which requires that same vold lock. To avoid these
+ // issues, use a separate thread for the RevocableFileDescriptor's requests, so that it can
+ // make progress independently of system_server.
+ static @NonNull Handler getRevocableFdHandler() {
+ synchronized (sLock) {
+ if (sRevocableFdHandler != null) {
+ return sRevocableFdHandler;
+ }
+ final HandlerThread t = new HandlerThread("BlobFuseLooper");
+ t.start();
+ sRevocableFdHandler = new Handler(t.getLooper());
+
+ return sRevocableFdHandler;
+ }
+ }
}
diff --git a/core/java/android/os/RevocableFileDescriptor.java b/core/java/android/os/RevocableFileDescriptor.java
index ac2cd60c81b2..7093c49fb4da 100644
--- a/core/java/android/os/RevocableFileDescriptor.java
+++ b/core/java/android/os/RevocableFileDescriptor.java
@@ -73,11 +73,26 @@ public class RevocableFileDescriptor {
init(context, fd);
}
+ public RevocableFileDescriptor(Context context, FileDescriptor fd, Handler handler)
+ throws IOException {
+ init(context, fd, handler);
+ }
+
/** {@hide} */
public void init(Context context, FileDescriptor fd) throws IOException {
+ init(context, fd, null);
+ }
+
+ /** {@hide} */
+ public void init(Context context, FileDescriptor fd, Handler handler) throws IOException {
mInner = fd;
- mOuter = context.getSystemService(StorageManager.class)
- .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+ StorageManager sm = context.getSystemService(StorageManager.class);
+ if (handler != null) {
+ mOuter = sm.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback,
+ handler);
+ } else {
+ mOuter = sm.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+ }
}
/**