Source-Changes-HG archive

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

[src/trunk]: src/sys/uvm Add an API to get a reference on the identity of an ...



details:   https://anonhg.NetBSD.org/src/rev/1af642c04276
branches:  trunk
changeset: 1009241:1af642c04276
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Sat Apr 18 03:27:13 2020 +0000

description:
Add an API to get a reference on the identity of an individual byte of
virtual memory, a "virtual object address".  This is not a reference to
a physical byte of memory, per se, but a reference to a byte residing
in a page, owned by a unique UVM object (either a uobj or an anon).  Two
separate address+addresses space tuples that reference the same byte in
an object (such as a location in a shared memory segment) will resolve
to equivalent virtual object addresses.  Even if the residency status
of the page changes, the virtual object address remains unchanged.

struct uvm_voaddr -- a structure that encapsulates this address reference.

uvm_voaddr_acquire() -- a function to acquire this address reference,
given a vm_map and a vaddr_t.

uvm_voaddr_release() -- a function to release this address reference.

uvm_voaddr_compare() -- a function to compare two such address references.

uvm_voaddr_acquire() resolves the COW status of the object address before
acquiring.

In collaboration with riastradh@ and chs@.

diffstat:

 sys/uvm/uvm_extern.h |   42 +++++++-
 sys/uvm/uvm_map.c    |  268 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 307 insertions(+), 3 deletions(-)

diffs (truncated from 352 to 300 lines):

diff -r 31c96346792b -r 1af642c04276 sys/uvm/uvm_extern.h
--- a/sys/uvm/uvm_extern.h      Fri Apr 17 22:53:52 2020 +0000
+++ b/sys/uvm/uvm_extern.h      Sat Apr 18 03:27:13 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uvm_extern.h,v 1.222 2020/03/22 18:32:42 ad Exp $      */
+/*     $NetBSD: uvm_extern.h,v 1.223 2020/04/18 03:27:13 thorpej Exp $ */
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -606,6 +606,40 @@
 extern struct vm_map *phys_map;
 
 /*
+ *     uvm_voaddr:
+ *
+ *     This structure encapsulates UVM's unique virtual object address
+ *     for an individual byte inside a pageable page. Pageable pages can
+ *     be owned by either a uvm_object (UVM_VOADDR_TYPE_OBJECT) or a
+ *     vm_anon (UVM_VOADDR_TYPE_ANON).
+ *
+ *     In each case, the byte offset into the owning object
+ *     (uvm_object or vm_anon) is included in the ID, so that
+ *     two different offsets into the same page have distinct
+ *     IDs.
+ *
+ *     Note that the page does not necessarily have to be resident
+ *     in order to know the virtual object address.  However, it
+ *     is required that any pending copy-on-write is resolved.
+ *
+ *     When someone wants a virtual object address, an extra reference
+ *     is taken on the owner while the caller uses the ID.  This
+ *     ensures that the identity is stable for the duration of its
+ *     use.
+ */
+struct uvm_voaddr {
+       enum {
+               UVM_VOADDR_TYPE_OBJECT = 1,
+               UVM_VOADDR_TYPE_ANON = 2,
+       } type;
+       union {
+               struct uvm_object *uobj;
+               struct vm_anon *anon;
+       };
+       voff_t offset;
+};
+
+/*
  * macros
  */
 
@@ -710,6 +744,12 @@
 void                   uvmspace_share(struct proc *, struct proc *);
 void                   uvmspace_unshare(struct lwp *);
 
+bool                   uvm_voaddr_acquire(struct vm_map *, vaddr_t,
+                           struct uvm_voaddr *);
+void                   uvm_voaddr_release(struct uvm_voaddr *);
+int                    uvm_voaddr_compare(const struct uvm_voaddr *,
+                           const struct uvm_voaddr *);
+
 void                   uvm_whatis(uintptr_t, void (*)(const char *, ...));
 
 /* uvm_meter.c */
diff -r 31c96346792b -r 1af642c04276 sys/uvm/uvm_map.c
--- a/sys/uvm/uvm_map.c Fri Apr 17 22:53:52 2020 +0000
+++ b/sys/uvm/uvm_map.c Sat Apr 18 03:27:13 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uvm_map.c,v 1.378 2020/04/10 17:26:46 ad Exp $ */
+/*     $NetBSD: uvm_map.c,v 1.379 2020/04/18 03:27:13 thorpej Exp $    */
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.378 2020/04/10 17:26:46 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.379 2020/04/18 03:27:13 thorpej Exp $");
 
 #include "opt_ddb.h"
 #include "opt_pax.h"
@@ -4781,6 +4781,270 @@
        }
 }
 
+/*
+ * uvm_voaddr_acquire: returns the virtual object address corresponding
+ * to the specified virtual address.
+ *
+ * => resolves COW so the true page identity is tracked.
+ *
+ * => acquires a reference on the page's owner (uvm_object or vm_anon)
+ */
+bool
+uvm_voaddr_acquire(struct vm_map * const map, vaddr_t const va,
+    struct uvm_voaddr * const voaddr)
+{
+       struct vm_map_entry *entry;
+       struct vm_anon *anon = NULL;
+       bool result = false;
+       bool exclusive = false;
+       void (*unlock_fn)(struct vm_map *);
+
+       UVMHIST_FUNC("uvm_voaddr_acquire"); UVMHIST_CALLED(maphist);
+       UVMHIST_LOG(maphist,"(map=%#jx,va=%jx)", (uintptr_t)map, va, 0, 0);
+
+       const vaddr_t start = trunc_page(va);
+       const vaddr_t end = round_page(va+1);
+
+ lookup_again:
+       if (__predict_false(exclusive)) {
+               vm_map_lock(map);
+               unlock_fn = vm_map_unlock;
+       } else {
+               vm_map_lock_read(map);
+               unlock_fn = vm_map_unlock_read;
+       }
+
+       if (__predict_false(!uvm_map_lookup_entry(map, start, &entry))) {
+               unlock_fn(map);
+               UVMHIST_LOG(maphist,"<- done (no entry)",0,0,0,0);
+               return false;
+       }
+
+       if (__predict_false(entry->protection == VM_PROT_NONE)) {
+               unlock_fn(map);
+               UVMHIST_LOG(maphist,"<- done (PROT_NONE)",0,0,0,0);
+               return false;
+       }
+
+       /*
+        * We have a fast path for the common case of "no COW resolution
+        * needed" whereby we have taken a read lock on the map and if
+        * we don't encounter any need to create a vm_anon then great!
+        * But if we do, we loop around again, instead taking an exclusive
+        * lock so that we can perform the fault.
+        *
+        * In the event that we have to resolve the fault, we do nearly the
+        * same work as uvm_map_pageable() does:
+        *
+        * 1: holding the write lock, we create any anonymous maps that need
+        *    to be created.  however, we do NOT need to clip the map entries
+        *    in this case.
+        *
+        * 2: we downgrade to a read lock, and call uvm_fault_wire to fault
+        *    in the page (assuming the entry is not already wired).  this
+        *    is done because we need the vm_anon to be present.
+        */
+       if (__predict_true(!VM_MAPENT_ISWIRED(entry))) {
+
+               bool need_fault = false;
+
+               /*
+                * perform the action of vm_map_lookup that need the
+                * write lock on the map: create an anonymous map for
+                * a copy-on-write region, or an anonymous map for
+                * a zero-fill region.
+                */
+               if (__predict_false(UVM_ET_ISSUBMAP(entry))) {
+                       unlock_fn(map);
+                       UVMHIST_LOG(maphist,"<- done (submap)",0,0,0,0);
+                       return false;
+               }
+               if (__predict_false(UVM_ET_ISNEEDSCOPY(entry) &&
+                   ((entry->max_protection & VM_PROT_WRITE) ||
+                    (entry->object.uvm_obj == NULL)))) {
+                       if (!exclusive) {
+                               /* need to take the slow path */
+                               KASSERT(unlock_fn == vm_map_unlock_read);
+                               vm_map_unlock_read(map);
+                               exclusive = true;
+                               goto lookup_again;
+                       }
+                       need_fault = true;
+                       amap_copy(map, entry, 0, start, end);
+                       /* XXXCDC: wait OK? */
+               }
+
+               /*
+                * do a quick check to see if the fault has already
+                * been resolved to the upper layer.
+                */
+               if (__predict_true(entry->aref.ar_amap != NULL &&
+                                  need_fault == false)) {
+                       amap_lock(entry->aref.ar_amap, RW_WRITER);
+                       anon = amap_lookup(&entry->aref, start - entry->start);
+                       if (__predict_true(anon != NULL)) {
+                               /* amap unlocked below */
+                               goto found_anon;
+                       }
+                       amap_unlock(entry->aref.ar_amap);
+                       need_fault = true;
+               }
+
+               /*
+                * we predict this test as false because if we reach
+                * this point, then we are likely dealing with a
+                * shared memory region backed by a uvm_object, in
+                * which case a fault to create the vm_anon is not
+                * necessary.
+                */
+               if (__predict_false(need_fault)) {
+                       if (exclusive) {
+                               vm_map_busy(map);
+                               vm_map_unlock(map);
+                               unlock_fn = vm_map_unbusy;
+                       }
+
+                       if (uvm_fault_wire(map, start, end,
+                                          entry->max_protection, 1)) {
+                               /* wiring failed */
+                               unlock_fn(map);
+                               UVMHIST_LOG(maphist,"<- done (wire failed)",
+                                           0,0,0,0);
+                               return false;
+                       }
+
+                       /*
+                        * now that we have resolved the fault, we can unwire
+                        * the page.
+                        */
+                       if (exclusive) {
+                               vm_map_lock(map);
+                               vm_map_unbusy(map);
+                               unlock_fn = vm_map_unlock;
+                       }
+
+                       uvm_fault_unwire_locked(map, start, end);
+               }
+       }
+
+       /* check the upper layer */
+       if (entry->aref.ar_amap) {
+               amap_lock(entry->aref.ar_amap, RW_WRITER);
+               anon = amap_lookup(&entry->aref, start - entry->start);
+               if (anon) {
+ found_anon:           KASSERT(anon->an_lock == entry->aref.ar_amap->am_lock);
+                       anon->an_ref++;
+                       KASSERT(anon->an_ref != 0);
+                       voaddr->type = UVM_VOADDR_TYPE_ANON;
+                       voaddr->anon = anon;
+                       voaddr->offset = va & PAGE_MASK;
+                       result = true;
+               }
+               amap_unlock(entry->aref.ar_amap);
+       }
+
+       /* check the lower layer */
+       if (!result && UVM_ET_ISOBJ(entry)) {
+               struct uvm_object *uobj = entry->object.uvm_obj;
+
+               KASSERT(uobj != NULL);
+               (*uobj->pgops->pgo_reference)(uobj);
+               voaddr->type = UVM_VOADDR_TYPE_OBJECT;
+               voaddr->uobj = uobj;
+               voaddr->offset = entry->offset + (va - entry->start);
+               result = true;
+       }
+
+       unlock_fn(map);
+
+       if (result) {
+               UVMHIST_LOG(maphist,
+                   "<- done OK (type=%jd,owner=#%jx,offset=%jx)",
+                   voaddr->type, voaddr->uobj, voaddr->offset, 0);
+       } else {
+               UVMHIST_LOG(maphist,"<- done (failed)",0,0,0,0);
+       }
+
+       return result;
+}
+
+/*
+ * uvm_voaddr_release: release the references held by the
+ * vitual object address.
+ */
+void
+uvm_voaddr_release(struct uvm_voaddr * const voaddr)
+{
+
+       switch (voaddr->type) {
+       case UVM_VOADDR_TYPE_OBJECT: {
+               struct uvm_object * const uobj = voaddr->uobj;
+
+               KASSERT(uobj != NULL);
+               KASSERT(uobj->pgops->pgo_detach != NULL);
+               (*uobj->pgops->pgo_detach)(uobj);
+               break;
+           }
+       case UVM_VOADDR_TYPE_ANON: {
+               struct vm_anon * const anon = voaddr->anon;
+
+               KASSERT(anon != NULL);
+               rw_enter(anon->an_lock, RW_WRITER);
+               KASSERT(anon->an_ref > 0);
+               anon->an_ref--;
+               if (anon->an_ref == 0) {
+                       uvm_anon_release(anon);
+               } else {
+                       rw_exit(anon->an_lock);



Home | Main Index | Thread Index | Old Index