]> rtime.felk.cvut.cz Git - l4.git/blobdiff - l4/pkg/plr/server/src/memory.cc
update
[l4.git] / l4 / pkg / plr / server / src / memory.cc
index 0b689a3ade1cf5ce38dbba89029d51cb2af8d9b0..eb79ed635da9a4aa3670ec0bf3f662e2de97efdc 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Memory management implementation
  *
- * (c) 2011-2012 Björn Döbel <doebel@os.inf.tu-dresden.de>,
+ * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
  *     economic rights: Technische Universität Dresden (Germany)
  * This file is part of TUD:OS and distributed under the terms of the
  * GNU General Public License 2.
@@ -42,10 +42,16 @@ void Romain::Region_ops::unmap(Region_handler const *h, l4_addr_t vaddr,
 
 void Romain::Region_ops::free(Region_handler const *h, l4_addr_t start, unsigned long size)
 {
-       (void)h;
-       (void)start;
-       (void)size;
-       enter_kdebug("ops::free");
+       if ((h->flags() & L4Re::Rm::Reserved))
+               return;
+
+       if (h->flags() & L4Re::Rm::Pager)
+               return;
+
+       L4::Cap<L4Re::Dataspace> ds(h->client_cap_idx());
+       ds->clear(h->offset() + start, size);
+       
+       //enter_kdebug("ops::free");
 }
 
 
@@ -66,6 +72,8 @@ void Romain::Region_ops::release(Region_handler const* h)
 Romain::Region_map::Region_map()
        : Base(0,0), _active_instance(Invalid_inst)
 {
+       pthread_mutex_init(&_mtx, 0);
+
        l4_kernel_info_t *kip = l4re_kip();
        MSG() << "kip found at 0x" << std::hex << kip
              << " having " << L4::Kip::Mem_desc::count(kip)
@@ -96,20 +104,81 @@ Romain::Region_map::Region_map()
 }
 
 
+/*
+ * Attach a replica region locally in the master AS.
+ *
+ * The Romain::Master manages replicas' region map. As a part of this, the master
+ * attaches a copy of every region attached to one of the replicas to its own
+ * address space. This local region is then used to service page faults caused by
+ * the replicas.
+ *
+ * For read-only replica dataspaces, the master creates a writable copy of the
+ * original region in a separate dataspace. This allows us to modify their content
+ * if necessary (e.g., setting debug breakpoints on read-only code).
+ *
+ * For writable dataspaces, the master simply attaches the same region that has
+ * already been attached to the replica AS into its own address space and maintains
+ * a copy for every replica.
+ *
+ * The replica copies lead to another problem: to reduce replication overhead, we
+ * would like to handle page faults using as few memory mappings as possible. To
+ * be able to do so, we however need to make sure that all replica copies are
+ * aligned to the same start address. The Romain::Master does this by ensuring
+ * that the replica address as well as the master-local addresses of the copies
+ * are aligned to the same offset within a minimally aligned region. The minimum
+ * alignment is defined by the Region_handler()'s MIN_ALIGNMENT member (XXX and
+ * should at some point become a configurable feature).
+ *
+ * The layout looks like this:
+ *
+ *  REPLICA:
+ *
+ * (1)      (2)                         (3)
+ *  +- - - - +---------------------------+
+ *  |        |                           |
+ *  +- - - - +---------------------------+
+ *
+ *  MASTER (per copy):
+ *
+ * (A)      (B)                         (C)
+ *  +- - - - +---------------------------+
+ *  |        |                           |
+ *  +- - - - +---------------------------+ 
+ *
+ *   (1)  correct minimum alignment
+ *   (2)  region.start()
+ *   (3)  region.end()
+ *
+ *   (A)  address aligned similar to (1)
+ *   (B)  local_region[inst]->start()
+ *   (C)  local_region[inst]->end()
+ *
+ *   MIN_ALIGNMENT_MASK := (1 << MIN_ALIGNMENT) - 1
+ *   (1) := (2) - [(2) & MIN_ALIGNMENT_MASK]
+ *   (A) := (B) - [(B) & MIN_ALIGNMENT_MASK]
+ *
+ *  Attaching then works by first reserving an area with the RM
+ *  that has the size (C) - (A). Then we attach the dataspace to
+ *  (B) and free the reserved area so that the remaining memory
+ *  can again be freely used.
+ */
 void *Romain::Region_map::attach_locally(void* addr, unsigned long size,
                                          Romain::Region_handler *hdlr,
                                          unsigned flags, unsigned char align)
 {
-       long r; (void)align; (void)addr;
-       Romain::Region_handler::Dataspace const *ds = &hdlr->memory(0);
+       long r; (void)addr;
+       Romain::Region_handler::Dataspace const *ds = &hdlr->local_memory(0);
        L4Re::Dataspace::Stats dsstat;
        (*ds)->info(&dsstat);
 
-#if 0
-       MSG() << "attaching DS " << std::hex << ds->cap()
+#if 1
+       MSG() << PURPLE
+                 << "attaching DS " << std::hex << ds->cap()
              << ", addr = "     << addr
              << ", flags = "    << flags << " (" << dsstat.flags << ")"
-             << ", size = "     << size;
+             << ", size = "     << size
+             << ", align = "    << (unsigned int)align
+             << NOCOLOR;
 #endif
 
        if (!ds->is_valid()) {
@@ -173,11 +242,9 @@ void *Romain::Region_map::attach_locally(void* addr, unsigned long size,
         */
        if (!(dsstat.flags & L4Re::Dataspace::Map_rw)) {
                //MSG() << "Copying to rw dataspace.";
-               L4::Cap<L4Re::Dataspace> mem = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
-               _check(!mem.is_valid(), "could not alloc ds cap");
-               //MSG() << "  mem_alloc(" << eff_size << ", " << std::hex << mem.cap() << ")";
-               r = L4Re::Env::env()->mem_alloc()->alloc(eff_size, mem);
-               _check(r != 0, "memory allocation failed");
+               L4::Cap<L4Re::Dataspace> mem;
+
+               Romain::Region_map::allocate_ds(&mem, eff_size);
 
                mem->copy_in(0, *ds, page_base, hdlr->offset() + size);
                ds        = &mem; // taking pointer to stack?? XXX
@@ -185,7 +252,7 @@ void *Romain::Region_map::attach_locally(void* addr, unsigned long size,
                page_base = 0;
                /*
                 * XXX: If we copied the dataspace above, what happens to the
-                *      original DS that was passed in? Shoudln't it be released?
+                *      original DS that was passed in? Shouldn't it be released?
                 *
                 *      No. It might still be attached elsewhere. Perhaps decrement
                 *      some refcnt? XXX check
@@ -199,15 +266,16 @@ void *Romain::Region_map::attach_locally(void* addr, unsigned long size,
         */
        flags &= ~L4Re::Rm::In_area;
 
-       l4_addr_t a = 0;
-       r = L4Re::Env::env()->rm()->attach(&a, eff_size,
-                                          L4Re::Rm::Search_addr | flags,
-                                          *ds, page_base);
-       _check(r != 0, "attach error");
+       l4_addr_t a = Romain::Region_map::attach_aligned(ds, eff_size,
+                                                        page_base, flags,
+                                                        hdlr->alignment(),
+                                                        hdlr->align_diff());
 
        Romain::Region reg(a, a+eff_size - 1);
        reg.touch_rw();
 
+       MSG() << "set_local_region(" << _active_instance << ", "
+                 << std::hex << reg.start() << ")";
        hdlr->set_local_region(_active_instance, reg);
        return (void*)(a + offset_in_page);
 }
@@ -217,23 +285,69 @@ void *Romain::Region_map::attach(void* addr, unsigned long size,
                                  unsigned flags, unsigned char align, bool shared)
 {
        void *ret = 0;
+
+       MSG() << std::hex << addr << " " << size << " " << (int)align << " " << flags;
+       MSG() << "cap " << std::hex << hdlr.client_cap_idx();
+
+       /*
+        * XXX: the only reason for this Region_handler to exist is to copy
+        *      the shared parameter into the handler. Shouldn't the caller
+        *      do this???
+        */
        Romain::Region_handler _handler(hdlr);
+       _handler.shared(shared);
+
+       /*
+        * First, do the attach() in the remote address space. Thereby we get
+        * the remote address and can then during local attaching make sure
+        * that the memory alignment of the local mapping matches the remote
+        * alignment. This allows us to play mapping tricks later.
+        * 
+        * Note, we can only do this if the Search_addr flag is set, otherwise
+        * we might hurt the client's expectations about the mem region.
+        */
+       if ((flags & L4Re::Rm::Search_addr) and 
+           !(flags & L4Re::Rm::In_area) and
+           (size > L4_SUPERPAGESIZE) and
+           (align < L4_LOG2_SUPERPAGESIZE)) {
+               align = L4_LOG2_SUPERPAGESIZE;
+               size  = l4_round_size(size, L4_SUPERPAGESHIFT);
+       }
+       
 
-       if (shared) {
-               //DEBUG() << "======> SHARED <======";
-               _handler.shared(true);
+       ret = Base::attach(addr, size, _handler, flags, align);
+       MSG() << "Base::attach = " << std::hex << ret << " hdlr.offs: " << _handler.offset();
+       if (ret == (void*)~0UL) {
+               INFO() << std::hex << addr << " " << size << " " << (int)align << " " << flags;
+               enter_kdebug("Attach error");
        }
 
+       /*
+        * The node is now present in the region map. This means we now need
+        * to continue working with the node's handler member instead of our
+        * local copy (because the attach() call has made a copy of it).
+        */
+       Romain::Region_map::Base::Node n = find((l4_addr_t)ret);
+       // and the region handler is const by default ... *grrrml*
+       Romain::Region_handler* theHandler = const_cast<Romain::Region_handler*>(&(n->second));
+       
+       /*
+        * Figure out the best possible alignment we can have between the local
+        * and the remote region. If the alignments match, we can later map more
+        * than one page at a time.
+        */
+       theHandler->alignToAddressAndSize(reinterpret_cast<l4_addr_t>(ret), size);
+
        /* Only attach locally, if this hasn't been done beforehand yet. */
-       if (!_handler.local_region(_active_instance).start()) {
-               void* a = attach_locally(addr, size, &_handler, flags, align);
+       if (!n->second.local_region(_active_instance).start()) {
+               void* a = attach_locally(addr, size, theHandler,
+                                        flags, theHandler->alignment());
                _check(a == 0, "Error in local attach");
        }
-       ret = Base::attach(addr, size, _handler, flags, align);
 
        MSG() << "new mapping (" << _active_instance << ") "
-             << "[" << std::hex << _handler.local_region(_active_instance).start()
-             << " - " << _handler.local_region(_active_instance).end()
+             << "[" << std::hex << n->second.local_region(_active_instance).start()
+             << " - " << n->second.local_region(_active_instance).end()
              << "] -> " << ret;
        //enter_kdebug("after attach");
 
@@ -242,28 +356,67 @@ void *Romain::Region_map::attach(void* addr, unsigned long size,
 
 
 
-#if 0
-int Romain::Region_map::detach(void* addr, unsigned long size, unsigned flags,
-                            L4Re::Util::Region* reg, Romain::Region_handler* h)
+int Romain::Region_map::detach(void* /* IN */ addr, unsigned long /* IN */ size,
+                               unsigned /* IN */ flags,
+                               L4Re::Util::Region* /* OUT */ reg,
+                               Romain::Region_handler* /* OUT */ h)
 {
-       MSG() << "addr " << std::hex << l4_addr_t(addr) << " " << size
-             << " " << flags;
-       MSG() << "active instance: " << _active_instance;
-       enter_kdebug("detach");
-       return -1;
+       /* First, do a lookup for the handler, because Base::Detach() will not always
+        * return the handler node, but we need it for detach later.
+        */
+       l4_addr_t srch = (l4_addr_t)addr;
+       Romain::Region_handler hd = Base::find(Region(srch, srch + size - 1))->second;
+       MSG() << "Client cap: " << std::hex << hd.client_cap_idx();
+       if (hd.client_cap_idx() == L4_INVALID_CAP) {
+               enter_kdebug("invalid cap!");
+       }
+
+       MSG() << std::hex << "Base::detach(" << (l4_addr_t)addr << " " << size << " " << flags << ")";
+       MSG() << std::hex << find((l4_addr_t)addr)->second.client_cap_idx();
+       int ret = Base::detach(addr, size, flags, reg, h);
+       MSG() << std::hex << "Base::detach(): " << ret << " r.start: " << reg->start() << " " << reg->size();
+
+       /*
+        * Iterate over replicas and detach() the replicas' mappings within
+        * the master AS.
+        */
+       for (unsigned idx = 0; idx < hd.local_region_count(); ++idx) {
+               if (hd.local_region(idx).start() == 0) {
+                       continue;
+               }
+
+               MSG() << "[" << std::hex
+                         << hd.local_region(idx).start() << " - "
+                         << hd.local_region(idx).end() << "] ==> "
+                         << "[" << reg->start() << " - "
+                         << reg->end() << "]";
+
+               L4::Cap<L4Re::Dataspace> memcap;
+               int r = L4Re::Env::env()->rm()->detach(hd.local_region(idx).start(),
+                                                      hd.local_region(idx).size(),
+                                                                                          &memcap, L4Re::This_task);
+               MSG() << "detached locally: " << r << " cap: " << std::hex << memcap.cap();
+
+               if (!hd.writable()) {
+                       /* For read-only regions, we only need to detach once */
+                       break;
+               }
+       }
+
+       return ret;
 }
-#endif
 
-bool Romain::Region_map::lazy_map_region(Romain::Region_map::Base::Node &n, unsigned inst)
+
+bool Romain::Region_map::lazy_map_region(Romain::Region_map::Base::Node &n, unsigned inst, bool iswritepf)
 {
        /* already mapped? */
        if (n->second.local_region(inst).start())
                return false;
 
-       DEBUGf(Romain::Log::Memory) << "start " <<  n->second.local_region(inst).start();
-       DEBUGf(Romain::Log::Memory) << "replica without yet established mapping.";
-       DEBUGf(Romain::Log::Memory) << "ro: " << n->second.is_ro();
-       DEBUGf(Romain::Log::Memory) << "shared: " << (n->second.shared() ? "true" : "false");
+       MSG() << "start " <<  n->second.local_region(inst).start();
+       MSG() << "replica without yet established mapping.";
+       MSG() << "ro: " << n->second.is_ro();
+       MSG() << "shared: " << (n->second.shared() ? "true" : "false");
 
        /*
         * As we found a node, we know there exists at least one replica
@@ -281,17 +434,17 @@ bool Romain::Region_map::lazy_map_region(Romain::Region_map::Base::Node &n, unsi
         * Case 1: region is read-only -> we share the mapping from
         *         the first node, because it was already established.
         */
-       
        if (n->second.is_ro() or rh->shared()) {
                /*
                 * XXX: Why is setting local_region and memory split up?
                 */
                rh->set_local_region(inst, n->second.local_region(existing));
-               rh->memory(inst, n->second.memory(existing));
+               rh->local_memory(inst, n->second.local_memory(existing));
        } else {
-               DEBUGf(Romain::Log::Memory) << "Copying existing mapping.";
-               bool b = copy_existing_mapping(*rh, existing, inst);
-               _check(!b, "error creating rw copy");
+                       MSG() << "Copying existing mapping.";
+                       bool b = copy_existing_mapping(*rh, existing, inst, iswritepf);
+                       _check(!b, "error creating rw copy");
        }
        return true;
 }
+