summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorEmery Hemingway <githubjunk@spam.works>2017-12-14 03:23:47 -0600
committerAndreas Rumpf <rumpf_a@web.de>2017-12-14 10:23:47 +0100
commit9e87531f041525b23ca12c7886412d762930005a (patch)
tree3ddaecfacf7bf7f13ac90207e96dfc0bd1353efc /lib
parent3546a89e8785051163d5ee28ed17d028072eb08c (diff)
downloadNim-9e87531f041525b23ca12c7886412d762930005a.tar.gz
Genode: constrain `osTryAllocPages` to RAM quota (#6883)
Genode software components all start with an explicit RAM resource quota
which may or may not be upgraded during runtime by the parent process.
With this patch `osTryAllocPages` will fail if allocation exceeds quotas
set by the parent and the `osAllocPages` procedure will trigger a
blocking request to the parent to increase quotas. The previous behavior
could potentially block both procedures indefinitely for a quota upgrade
rather than fail and trigger garbage collection.

This patch also adds tracking of Genode dataspace mappings into the
component address space so they can be detached and freed.
Diffstat (limited to 'lib')
-rw-r--r--lib/system/genodealloc.nim114
-rw-r--r--lib/system/osalloc.nim12
2 files changed, 115 insertions, 11 deletions
diff --git a/lib/system/genodealloc.nim b/lib/system/genodealloc.nim
new file mode 100644
index 000000000..3646a842d
--- /dev/null
+++ b/lib/system/genodealloc.nim
@@ -0,0 +1,114 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Low level dataspace allocator for Genode.
+
+when not defined(genode):
+  {.error: "Genode only module".}
+
+type DataspaceCapability {.
+  importcpp: "Genode::Dataspace_capability", pure.} = object
+
+type
+  Map = object
+    attachment: pointer
+    size: int
+    ds: DataspaceCapability
+
+  SlabMeta = object
+    next: ptr MapSlab
+    ds: DataspaceCapability
+
+  MapSlab = object
+    meta: SlabMeta
+    maps: array[1,Map]
+
+const SlabBackendSize = 4096
+
+proc ramAvail(): int {.
+  importcpp: "genodeEnv->pd().avail_ram().value".}
+  ## Return number of bytes available for allocation.
+
+proc capsAvail(): int {.
+  importcpp: "genodeEnv->pd().avail_caps().value".}
+  ## Return the number of available capabilities.
+  ## Each dataspace allocation consumes a capability.
+
+proc allocDataspace(size: int): DataspaceCapability {.
+  importcpp: "genodeEnv->pd().alloc(@)".}
+  ## Allocate a dataspace and its capability.
+
+proc attachDataspace(ds: DataspaceCapability): pointer {.
+  importcpp: "genodeEnv->rm().attach(@)".}
+  ## Attach a dataspace into the component address-space.
+
+proc detachAddress(p: pointer) {.
+  importcpp: "genodeEnv->rm().detach(@)".}
+  ## Detach a dataspace from the component address-space.
+
+proc freeDataspace(ds: DataspaceCapability) {.
+  importcpp: "genodeEnv->pd().free(@)".}
+  ## Free a dataspace.
+
+proc newMapSlab(): ptr MapSlab =
+  let
+    ds = allocDataspace SlabBackendSize
+    p = attachDataspace ds
+  result = cast[ptr MapSlab](p)
+  result.meta.ds = ds
+
+iterator items(s: ptr MapSlab): ptr Map =
+  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
+  for i in 0 .. <mapCount:
+    yield s.maps[i].addr
+
+var slabs: ptr MapSlab
+
+proc osAllocPages(size: int): pointer =
+  if slabs.isNil:
+    slabs = newMapSlab()
+  var
+    slab = slabs
+    map: ptr Map
+  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
+  block findFreeMap:
+    while true:
+      # lookup first free spot in slabs
+      for m in slab.items:
+        if m.attachment.isNil:
+          map = m
+          break findFreeMap
+      if slab.meta.next.isNil:
+        slab.meta.next = newMapSlab()
+          # tack a new slab on the tail
+      slab = slab.meta.next
+        # move to next slab in linked list
+  map.ds = allocDataspace size
+  map.size = size
+  map.attachment = attachDataspace map.ds
+  result = map.attachment
+
+proc osTryAllocPages(size: int): pointer =
+  if ramAvail() >= size and capsAvail() > 1:
+    result = osAllocPages size
+
+proc osDeallocPages(p: pointer; size: int) =
+  var slab = slabs
+  while not slab.isNil:
+    # lookup first free spot in slabs
+    for m in slab.items:
+      if m.attachment == p:
+        if m.size != size:
+          echo "cannot partially detach dataspace"
+          quit -1
+        detachAddress m.attachment
+        freeDataspace m.ds
+        m[] = Map()
+        return
+    slab = slab.meta.next
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 444f11306..1ad4cf695 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -78,17 +78,7 @@ when defined(emscripten):
     munmap(mmapDescr.realPointer, mmapDescr.realSize)
 
 elif defined(genode):
-
-  proc osAllocPages(size: int): pointer {.
-   importcpp: "genodeEnv->rm().attach(genodeEnv->ram().alloc(@))".}
-
-  proc osTryAllocPages(size: int): pointer =
-    {.emit: """try {""".}
-    result = osAllocPages size
-    {.emit: """} catch (...) { }""".}
-
-  proc osDeallocPages(p: pointer, size: int) {.
-    importcpp: "genodeEnv->rm().detach(#)".}
+  include genodealloc # osAllocPages, osTryAllocPages, osDeallocPages
 
 elif defined(posix):
   const