summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2023-05-04 21:34:07 +0300
committerIvan Maidanski <ivmai@mail.ru>2023-05-04 21:43:12 +0300
commitd654f40deaa60b9bcaa5177963aec76974745897 (patch)
tree1be9c4b31e8f45b4c5fbff2b40ff9014d8a4ea1a
parent53f132650ac49289c4a8e2b0cebbfab5e08a205b (diff)
downloadbdwgc-d654f40deaa60b9bcaa5177963aec76974745897.tar.gz
Workaround a malfunction of soft-dirty bits clearing on Power9
(fix of commit c1bf1b973) Issue #479 (bdwgc). Make a page dirty twice in detect_soft_dirty_supported() clearing all the soft-dirty bits in the middle. If the 2nd write to a page is not noticeable, then fallback to mprotect-based VDB. This a workaround for a bug observed, at least, in Fedora 36 kernel on Power9 CPU. * os_dep.c [SOFT_VDB] (clear_soft_dirty_bits): New static function (move part of code from GC_soft_read_dirty). * os_dep.c [SOFT_VDB] (detect_soft_dirty_supported): Call clear_soft_dirty_bits() and retry changing *vaddr to check whether the latter is reflected in pagemap file; add comment. * os_dep.c [SOFT_VDB] (GC_soft_read_dirty): Call clear_soft_dirty_bits.
-rw-r--r--os_dep.c46
1 files changed, 30 insertions, 16 deletions
diff --git a/os_dep.c b/os_dep.c
index 25acece8..e38f9fc7 100644
--- a/os_dep.c
+++ b/os_dep.c
@@ -3833,6 +3833,16 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded)
}
# endif /* CAN_HANDLE_FORK */
+ /* Clear soft-dirty bits from the task's PTEs. */
+ static void clear_soft_dirty_bits(void)
+ {
+ ssize_t res = write(clear_refs_fd, "4\n", 2);
+
+ if (res != 2)
+ ABORT_ARG1("Failed to write to /proc/self/clear_refs",
+ ": errno= %d", res < 0 ? errno : 0);
+ }
+
/* The bit 55 of the 64-bit qword of pagemap file is the soft-dirty one. */
# define PM_SOFTDIRTY_MASK ((pagemap_elem_t)1 << 55)
@@ -3841,18 +3851,28 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded)
off_t fpos;
pagemap_elem_t buf[1];
- *vaddr = 1; /* make it dirty */
-
- /* Read the relevant PTE from the pagemap file. */
GC_ASSERT(GC_log_pagesize != 0);
+ *vaddr = 1; /* make it dirty */
fpos = (off_t)(((word)vaddr >> GC_log_pagesize) * sizeof(pagemap_elem_t));
- if (lseek(pagemap_fd, fpos, SEEK_SET) == (off_t)(-1))
- return FALSE;
- if (PROC_READ(pagemap_fd, buf, sizeof(buf)) != (int)sizeof(buf))
- return FALSE;
- /* Is the soft-dirty bit set? */
- return (buf[0] & PM_SOFTDIRTY_MASK) != 0;
+ for (;;) {
+ /* Read the relevant PTE from the pagemap file. */
+ if (lseek(pagemap_fd, fpos, SEEK_SET) == (off_t)(-1))
+ return FALSE;
+ if (PROC_READ(pagemap_fd, buf, sizeof(buf)) != (int)sizeof(buf))
+ return FALSE;
+
+ /* Is the soft-dirty bit unset? */
+ if ((buf[0] & PM_SOFTDIRTY_MASK) == 0) return FALSE;
+
+ if (0 == *vaddr) break;
+ /* Retry to check that writing to clear_refs works as expected. */
+ /* This malfunction of the soft-dirty bits implementation is */
+ /* observed on some Linux kernels on Power9 (e.g. in Fedora 36). */
+ clear_soft_dirty_bits();
+ *vaddr = 0;
+ }
+ return TRUE; /* success */
}
# ifndef NO_SOFT_VDB_LINUX_VER_RUNTIME_CHECK
@@ -4037,8 +4057,6 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded)
GC_INLINE void GC_soft_read_dirty(GC_bool output_unneeded)
{
- ssize_t res;
-
GC_ASSERT(I_HOLD_LOCK());
# ifndef THREADS
/* Similar as for GC_proc_read_dirty. */
@@ -4085,11 +4103,7 @@ GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded)
# endif
}
- /* Clear soft-dirty bits from the task's PTEs. */
- res = write(clear_refs_fd, "4\n", 2);
- if (res != 2)
- ABORT_ARG1("Failed to write to /proc/self/clear_refs",
- ": errno= %d", res < 0 ? errno : 0);
+ clear_soft_dirty_bits();
}
#endif /* SOFT_VDB */