Skip to content

Commit

Permalink
Workaround a malfunction of soft-dirty bits clearing on Power9
Browse files Browse the repository at this point in the history
(fix of commit c1bf1b9)

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.
  • Loading branch information
ivmai committed May 4, 2023
1 parent 53f1326 commit d654f40
Showing 1 changed file with 30 additions and 16 deletions.
46 changes: 30 additions & 16 deletions os_dep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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 */

Expand Down

0 comments on commit d654f40

Please sign in to comment.