kernel - Fix pmap placemarker timing race
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 23 Jun 2019 15:24:24 +0000 (08:24 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 23 Jun 2019 15:48:08 +0000 (08:48 -0700)
commit92414ddf43087593c70a5b4011019fa8ec8cce80
tree19373223dc12fc259881a310f0ce630ef5643f4f
parent63163bc11de6652726169dc550c7adf51f2d38fb
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
sys/platform/pc64/x86_64/pmap.c