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.
This commit is contained in:
Emery Hemingway
2017-12-14 03:23:47 -06:00
committed by Andreas Rumpf
parent 3546a89e87
commit 9e87531f04
2 changed files with 115 additions and 11 deletions

114
lib/system/genodealloc.nim Normal file
View File

@@ -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

View File

@@ -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