Handle userfaultfd API ioctl on older kernel hosts
On older kernels on host it's possible that minor-fault feature doesn't
exist, in which case the ioctl may fail with EINVAL. This CL detects the
failure reason and retries the ioctl without it.
Bug: 160737021
Test: art/test/testrunner/testrunner.py --host
Change-Id: Ibd55933ec985f5e62fdaa9dd85ed57d544546d18
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index e7ca948..464530d 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -189,11 +189,29 @@
<< ") and therefore falling back to stop-the-world compaction.";
} else {
DCHECK(IsValidFd(uffd_));
+ uint64_t requested_features = UFFD_FEATURE_MISSING_SHMEM | UFFD_FEATURE_MINOR_SHMEM;
// Get/update the features that we want in userfaultfd
- struct uffdio_api api = {.api = UFFD_API,
- .features = UFFD_FEATURE_MISSING_SHMEM | UFFD_FEATURE_MINOR_SHMEM};
- CHECK_EQ(ioctl(uffd_, UFFDIO_API, &api), 0)
- << "ioctl_userfaultfd: API: " << strerror(errno);
+ struct uffdio_api api = {.api = UFFD_API, .features = requested_features, .ioctls = 0};
+ int ret = ioctl(uffd_, UFFDIO_API, &api);
+ if (UNLIKELY(ret != 0)) {
+ if (errno == EINVAL) {
+ // Confirm that this didn't happen because uffd was already
+ // initialized by a previous invocation of API ioctl. In that case
+ // 'api.ioctls != 0'.
+ CHECK_EQ(api.ioctls, 0u)
+ << "ioctl_userfaultfd: API: " << strerror(errno) << ". uffd already initialized";
+ // This means we are requesting some feature which doesn't exist
+ // on this kernel. This can't be with UFFD_FEATURE_MISSING_SHMEM
+ // as it's one of the earliest features introduced in userfaultfd.
+ //
+ // Invoke the ioctl again, but without UFFD_FEATURE_MINOR_SHMEM.
+ api.features &= ~UFFD_FEATURE_MINOR_SHMEM;
+ CHECK_EQ(ioctl(uffd_, UFFDIO_API, &api), 0)
+ << "ioctl_userfaultfd: API: failed again with: " << strerror(errno);
+ } else {
+ LOG(FATAL) << "ioctl_userfaultfd: API: " << strerror(errno);
+ }
+ }
// Missing userfaults on shmem should always be available.
DCHECK_NE(api.features & UFFD_FEATURE_MISSING_SHMEM, 0u);
uffd_minor_fault_supported_ =