Page MenuHomeFreeBSD

riscv: Fix a race in pmap_pinit()
ClosedPublic

Authored by markj on Feb 3 2022, 4:06 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sat, Dec 7, 5:01 AM
Unknown Object (File)
Thu, Dec 5, 7:17 PM
Unknown Object (File)
Wed, Dec 4, 8:03 PM
Unknown Object (File)
Wed, Dec 4, 1:09 PM
Unknown Object (File)
Nov 14 2024, 12:12 AM
Unknown Object (File)
Oct 12 2024, 9:22 AM
Unknown Object (File)
Sep 26 2024, 1:20 PM
Unknown Object (File)
Sep 25 2024, 4:23 AM
Subscribers

Details

Summary

All pmaps share the top half of the address space. With 3-level page
tables, the top-level kernel map entries are not static: they might
change if the kernel map is extended (via pmap_growkernel()) or a 1GB
mapping in the direct map is demoted (doesn't seem to be implemented
yet?). Thus the riscv pmap maintains the allpmaps list to synchronize
updates to top-level entries.

When a pmap is created, it is inserted into this list after copying
top-level entries from the kernel pmap. The copying is done without
holding the allpmaps lock, and I believe it's possible for pmap_pinit()
to race with kernel map updates. In particular, if a thread is
modifying L1 entries, and a concurrent pmap_pinit() copies the old
version of the entries, it might not receive the update.

Fix the problem by copying the kernel map entries after inserting the
pmap into the list. This ensures that the nascent pmap always receives
updates. It doesn't seem necessary to perform the copy while holding
the allpmaps lock.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

markj requested review of this revision.Feb 3 2022, 4:06 PM
mhorne added a subscriber: mhorne.

"It doesn't seem necessary to perform the copy while holding the allpmaps lock." -- I had to think about this for a while, but I agree.

This revision is now accepted and ready to land.Feb 3 2022, 6:34 PM

What happens if pmap_distribute_l1 runs whilst the memcpy is taking place and "overtakes" the memcpy? Won't we then potentially clobber newer entries with stale copies?

What happens if pmap_distribute_l1 runs whilst the memcpy is taking place and "overtakes" the memcpy? Won't we then potentially clobber newer entries with stale copies?

A thread updating the kernel map will (and must):

  1. update the reference L1 entry
  2. acquire the allpmaps lock
  3. update user pmaps
  4. release the all pmaps lock

so the update will be visible to the copying thread either way. Note that threads updating the kernel map are serialized by the kernel pmap lock, with one exception in pmap_growkernel(). But pmap_growkernel() only ever extends the kernel map, and I believe it should be impossible for two threads to race to update the same kernel L1 entry.

This revision was automatically updated to reflect the committed changes.