Files
Nim/lib/system/genodealloc.nim
Emery Hemingway 9e87531f04 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.
2017-12-14 10:23:47 +01:00

115 lines
3.0 KiB
Nim

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