git: kernel - Fix pmap placemarker timing race
Matthew Dillon
dillon at crater.dragonflybsd.org
Sun Jun 23 08:56:54 PDT 2019
commit 92414ddf43087593c70a5b4011019fa8ec8cce80
Author: Matthew Dillon <dillon at apollo.backplane.com>
Date: Sun Jun 23 08:24:24 2019 -0700
kernel - Fix pmap placemarker timing race
* Fix a timing race that could cause a thread to get stuck in
"pvplw" indefinitely. The timing window is very short and the
race is fairly difficult to reproduce.
* For this race to occur, more than one interaction must take
place on other cpus against the placemarker being waited on
by pv_placemarker_wait(). It takes multiple interactions for
the WAKEUP bit to be lost.
The first interaction can clear the WAKEUP bit and issue a
wakeup() before we manage to interlock. The second interaction
can then reserve the placemarker so our conditional fails and
we tsleep(). The result is that we block forever.
pv_placemarker_wait(pmap_t pmap, vm_pindex_t *pmark)
{
if (*pmark != PM_NOPLACEMARK) {
atomic_set_long(pmark, PM_PLACEMARK_WAKEUP);
tsleep_interlock(pmark, 0);
if (*pmark != PM_NOPLACEMARK)
tsleep(pmark, PINTERLOCKED, "pvplw", 0);
}
}
Just moving the interlock to before setting the flag is not
sufficient due to cuteness on my part in overloading the WAKEUP
bit on top of NOPLACEMARK (NOPLACEMARK is '-1').
* The solution is to both properly order the interlock AND use
a cmpset loop to prevent accidently setting the WAKEUP bit on
a marker that has already been released.
mark = *pmark;
cpu_ccfence();
while (mark != PM_NOPLACEMARK) {
tsleep_interlock(pmark, 0);
if (atomic_fcmpset_long(pmark, &mark,
mark | PM_PLACEMARK_WAKEUP)) {
tsleep(pmark, PINTERLOCKED, "pvplw", 0);
break;
}
}
Reported-by: tuxillo
Summary of changes:
sys/platform/pc64/x86_64/pmap.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff/92414ddf43087593c70a5b4011019fa8ec8cce80
--
DragonFly BSD source repository
More information about the Commits
mailing list