tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

removing mappings of uvm objects or device pages



How can I remove all virtual mappings of a range of a uvm object, or
all virtual mappings of a physical page that is a device page rather
than a physical RAM page?

- I can't uvm_unmap(vm_map, start, end) because I don't know every
  vm_map into which this uvm object has been mapped and where,
  although if I had a way to list weak references to vm_maps perhaps I
  could keep books about this.

- I can't pmap_page_protect(vm_page, VM_PROT_NONE) because there is no
  struct vm_page for the pages in question -- they're device pages,
  not system RAM pages.

Background (with apologies for the length; this is a little tricky):

Graphics drivers involve buffers, represented by UVM objects, that can
be mapped into the GPU virtual address space or into one or more of
the various CPU address spaces.  Sometimes the driver needs to free up
some GPU virtual address space, so it will choose an inactive buffer
to evict, say at V_gpu, and unmap the GPU page table entry for V_gpu.

If the buffer is not mapped into any CPU virtual address space, all is
well and good.  But if it is mapped into a CPU virtual address space,
say at the virtual address V_cpu, it's a little tricky.

While shared between GPU and CPU virtual address spaces, graphics
buffers are backed by physical pages of system RAM, say at the
physical address P_ram.  The GPU page table's entry for V_gpu points
at P_ram.  But the buffer's fault handler for V_cpu doesn't map it to
P_ram -- it maps V_cpu to a special physical address P_aper in a
region of physical memory called the `aperture', in order to
participate in the memory coherency protocol with the GPU.

The aperture is a contiguous region of physical memory which, when
viewed by the CPU, looks just like (a prefix of) the GPU's virtual
address space: if the GPU writes something to virtual page 3, the CPU
can see what it wrote by mapping the physical page aper_start + 3 to
some CPU virtual page and reading that.

Evicting a buffer from the GPU's adress space does not mean evicting
it from all CPU address spaces.  However, it does mean that if a CPU
virtual address space had V_cpu mapped to P_aper, that mapping must be
removed so that the CPU doesn't see through P_aper whatever random
buffer has since been mapped into the GPU's address space at V_gpu.
Instead, the CPU must re-fault on subsequent access to V_cpu in order
to continue using the buffer.

That is, evicting the buffer from the GPU at V_gpu requires also
removing the mapping of V_cpu to P_aper in the CPU virtual address
space (and there may be many such mappings).

So to allocate a buffer and share it between the GPU at V_gpu and the
CPU at V_cpu, the system will

- allocate a page P_ram from system RAM,
- program P_ram into the GPU PTE for V_gpu, and
- program P_aper (= aper_start + V_gpu) into the CPU PTE for V_cpu.

When evicting this buffer from the GPU, the graphics driver, via
records about the buffer, knows V_gpu, P_ram, and P_aper, so it can
easily remove the GPU PTE for V_gpu.

What the driver doesn't know is V_cpu, but it needs to remove the CPU
PTE for V_cpu.  And currently I don't know a way to do this.

- If P_aper were a page of normal physical RAM, then the driver could
  just pmap_page_protect(PHYS_TO_VM_PAGE(P_aper), VM_PROT_NONE) and
  rely on the pmap(9) P->V bookkeeping to do this.  But P_aper is in
  device memory, not RAM, so pmap(9)'s P->V bookkeeping excludes it.

(OpenBSD lets drivers uvm_page_physload pages with a flag marking them
as device memory so that pmap(9)'s P->V bookkeeping will include them,
but uvm_pagealloc will not dole them out.  This requires creating
struct vm_pages for every page in the aperture for no other purpose,
and the aperture is easily hundreds of megabytes or gigabytes.)

- If a uvm object kept books everywhere it had been mapped, then the
  driver could just uvm_object_unmap(uobj, 0, size) or something.  But
  uvm objects don't keep books about that.

(Linux's analogue of uvm objects keep books about this and support an
operation unmap_mapping_range(obj, 0, size) to do exactly this.)

If we had a way to list weak references to uvm_maps or uvm_map_entries
or pmaps or something, then perhaps I could add bookkeeping to the
graphics drivers.  But I don't see an obvious way to do that.

One possibility would be to add an operation to uvm_pagerops called,
say, pgo_unmap(uobj, vm_map, start, end), which uvm_unmap_detach would
call just before pgo_detach.  That way, my pgo_fault could remember
the vm_maps and vaddrs; my eviction routine could go through them to
pmap_remove or uvm_map_protect the vaddrs; and my pgo_unmap could
forget the vm_map and vaddrs.  I haven't worked out the details,
though.

Thoughts?


Home | Main Index | Thread Index | Old Index