mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 15:32:25 +00:00
gpu/intel: deal with insufficient amount of CAPS
'Out_of_ram' was so far the only exception a client had to deal with during buffer managment. Allocating memory, however, does not only consume RAM quota but CAP quota as well. This commit tries to mitigate that shortcoming by reflecting the 'Out_of_caps' state back to the client. Furthermore it allows for resource accounting on certain client allocations, e.g. buffers. Fixes #4284.
This commit is contained in:
parent
f6d845e630
commit
6093f8ad81
@ -253,12 +253,20 @@ class Drm_call
|
||||
Genode::Hex(buffer.gpu_vaddr.addr), " vs ",
|
||||
Genode::Hex(vaddr.addr));
|
||||
|
||||
/* XXX out of cap XXX */
|
||||
bool const ppgtt = Genode::retry<Gpu::Session::Out_of_ram>(
|
||||
[&]() { return _gpu_session.map_buffer_ppgtt(buffer.id(),
|
||||
Utils::limit_to_48bit(vaddr.addr)); },
|
||||
[&]() { _gpu_session.upgrade_ram(4096); }
|
||||
);
|
||||
[&]() {
|
||||
return Genode::retry<Gpu::Session::Out_of_caps>(
|
||||
[&] () {
|
||||
return _gpu_session.map_buffer_ppgtt(buffer.id(),
|
||||
Utils::limit_to_48bit(vaddr.addr));
|
||||
},
|
||||
[&] () {
|
||||
_gpu_session.upgrade_caps(2);
|
||||
});
|
||||
},
|
||||
[&] () {
|
||||
_gpu_session.upgrade_ram(4096);
|
||||
});
|
||||
|
||||
if (!ppgtt) {
|
||||
Genode::error("could not insert buffer into PPGTT");
|
||||
@ -286,12 +294,17 @@ class Drm_call
|
||||
Buffer *buffer = nullptr;
|
||||
Genode::retry<Gpu::Session::Out_of_ram>(
|
||||
[&] () {
|
||||
buffer =
|
||||
new (&_heap) Buffer(_gpu_session, size, _buffer_space);
|
||||
Genode::retry<Gpu::Session::Out_of_caps>(
|
||||
[&] () {
|
||||
buffer =
|
||||
new (&_heap) Buffer(_gpu_session, size, _buffer_space);
|
||||
},
|
||||
[&] () {
|
||||
_gpu_session.upgrade_caps(2);
|
||||
});
|
||||
},
|
||||
[&] () {
|
||||
_gpu_session.upgrade_ram(donate);
|
||||
donate /= 4;
|
||||
});
|
||||
|
||||
if (buffer)
|
||||
@ -339,13 +352,25 @@ class Drm_call
|
||||
}
|
||||
|
||||
try {
|
||||
_gpu_session.upgrade_ram(4096);
|
||||
b.map_cap = _gpu_session.map_buffer(b.id(), true,
|
||||
Gpu::Mapping_attributes::rw());
|
||||
b.map_offset = static_cast<Offset>(_env.rm().attach(b.map_cap));
|
||||
offset = b.map_offset;
|
||||
Genode::retry<Gpu::Session::Out_of_ram>(
|
||||
[&]() {
|
||||
Genode::retry<Gpu::Session::Out_of_caps>(
|
||||
[&] () {
|
||||
b.map_cap = _gpu_session.map_buffer(b.id(), true,
|
||||
Gpu::Mapping_attributes::rw());
|
||||
b.map_offset = static_cast<Offset>(_env.rm().attach(b.map_cap));
|
||||
offset = b.map_offset;
|
||||
|
||||
_available_gtt_size -= b.size;
|
||||
},
|
||||
[&] () {
|
||||
_gpu_session.upgrade_caps(2);
|
||||
});
|
||||
},
|
||||
[&] () {
|
||||
_gpu_session.upgrade_ram(4096);
|
||||
});
|
||||
|
||||
_available_gtt_size -= b.size;
|
||||
} catch (...) {
|
||||
if (b.map_cap.valid())
|
||||
_gpu_session.unmap_buffer(b.id());
|
||||
|
@ -74,6 +74,7 @@ struct Igd::Device_info
|
||||
struct Igd::Device
|
||||
{
|
||||
struct Unsupported_device : Genode::Exception { };
|
||||
struct Out_of_caps : Genode::Exception { };
|
||||
struct Out_of_ram : Genode::Exception { };
|
||||
struct Could_not_map_buffer : Genode::Exception { };
|
||||
|
||||
@ -101,6 +102,43 @@ struct Igd::Device
|
||||
return _pci.alloc_dma_buffer(size, Genode::UNCACHED); });
|
||||
}
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size,
|
||||
Cap_quota_guard &caps_guard,
|
||||
Ram_quota_guard &ram_guard) override
|
||||
{
|
||||
/*
|
||||
* For now we only reflect quota exceptions on explicit user
|
||||
* allocations, e.g., buffers.
|
||||
*/
|
||||
try {
|
||||
Genode::size_t donate = size;
|
||||
return retry<Platform::Out_of_ram>(
|
||||
[&] () {
|
||||
return retry<Platform::Out_of_caps>(
|
||||
[&] () {
|
||||
return _pci.alloc_dma_buffer(size, Genode::UNCACHED);
|
||||
},
|
||||
[&] () {
|
||||
enum { UPGRADE_CAP_QUOTA = 2, };
|
||||
Cap_quota const caps { 2 };
|
||||
caps_guard.withdraw(caps);
|
||||
_pci.upgrade_caps(caps.value);
|
||||
}
|
||||
);
|
||||
},
|
||||
[&] () {
|
||||
Ram_quota const ram { donate };
|
||||
ram_guard.withdraw(ram);
|
||||
_pci.upgrade_ram(ram.value);
|
||||
}
|
||||
);
|
||||
} catch (Ram_quota_guard::Limit_exceeded) {
|
||||
throw Out_of_ram();
|
||||
} catch (Cap_quota_guard::Limit_exceeded) {
|
||||
throw Out_of_caps();
|
||||
}
|
||||
}
|
||||
|
||||
void free(Ram_dataspace_capability cap) override
|
||||
{
|
||||
if (!cap.valid()) {
|
||||
@ -445,11 +483,13 @@ struct Igd::Device
|
||||
|
||||
Engine(Igd::Device &device,
|
||||
uint32_t id,
|
||||
Allocator &alloc)
|
||||
Allocator &alloc,
|
||||
Cap_quota_guard &caps_guard,
|
||||
Ram_quota_guard &ram_guard)
|
||||
:
|
||||
ctx (device._env.rm(), alloc, device, CONTEXT::CONTEXT_PAGES, 1 /* omit GuC page */),
|
||||
ring(device._env.rm(), alloc, device, CONTEXT::RING_PAGES, 0),
|
||||
ppgtt_allocator(device._env.rm(), device._pci_backend_alloc),
|
||||
ppgtt_allocator(device._env.rm(), device._pci_backend_alloc, caps_guard, ram_guard),
|
||||
ppgtt_scratch(device._pci_backend_alloc)
|
||||
{
|
||||
/* PPGTT */
|
||||
@ -559,11 +599,13 @@ struct Igd::Device
|
||||
}
|
||||
|
||||
Vgpu(Device &device, Allocator &alloc,
|
||||
Ram_allocator &ram, Region_map &rm)
|
||||
Ram_allocator &ram, Region_map &rm,
|
||||
Cap_quota_guard &caps_guard,
|
||||
Ram_quota_guard &ram_guard)
|
||||
:
|
||||
_device(device),
|
||||
_id(_id_alloc()),
|
||||
rcs(_device, _id + Rcs_context::HW_ID, alloc),
|
||||
rcs(_device, _id + Rcs_context::HW_ID, alloc, caps_guard, ram_guard),
|
||||
_info_dataspace(ram, rm, INFO_SIZE)
|
||||
{
|
||||
_device.vgpu_created();
|
||||
@ -843,16 +885,9 @@ struct Igd::Device
|
||||
Genode::Page_flags pf;
|
||||
pf.writeable = Genode::Writeable::RW;
|
||||
|
||||
try {
|
||||
rcs.ppgtt->insert_translation(vo, pa, size, pf,
|
||||
&rcs.ppgtt_allocator,
|
||||
&rcs.ppgtt_scratch.pdp);
|
||||
} catch (Igd::Ppgtt_allocator::Out_of_memory) {
|
||||
throw Igd::Device::Out_of_ram();
|
||||
} catch (...) {
|
||||
/* Double_insertion and the like */
|
||||
throw Igd::Device::Could_not_map_buffer();
|
||||
}
|
||||
rcs.ppgtt->insert_translation(vo, pa, size, pf,
|
||||
&rcs.ppgtt_allocator,
|
||||
&rcs.ppgtt_scratch.pdp);
|
||||
}
|
||||
|
||||
void rcs_unmap_ppgtt(addr_t vo, size_t size)
|
||||
@ -1313,9 +1348,11 @@ struct Igd::Device
|
||||
* \throw Out_of_memory
|
||||
*/
|
||||
Genode::Dataspace_capability alloc_buffer(Allocator &,
|
||||
size_t const size)
|
||||
size_t const size,
|
||||
Cap_quota_guard &cap_guard,
|
||||
Ram_quota_guard &ram_guard)
|
||||
{
|
||||
return _pci_backend_alloc.alloc(size);
|
||||
return _pci_backend_alloc.alloc(size, cap_guard, ram_guard);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1347,13 +1384,9 @@ struct Igd::Device
|
||||
Genode::Dataspace_capability cap, bool aperture)
|
||||
{
|
||||
size_t const size = Genode::Dataspace_client(cap).size();
|
||||
try {
|
||||
size_t const num = size / PAGE_SIZE;
|
||||
Ggtt::Offset const offset = _ggtt->find_free(num, aperture);
|
||||
return map_dataspace_ggtt(guard, cap, offset);
|
||||
} catch (...) {
|
||||
throw Could_not_map_buffer();
|
||||
}
|
||||
size_t const num = size / PAGE_SIZE;
|
||||
Ggtt::Offset const offset = _ggtt->find_free(num, aperture);
|
||||
return map_dataspace_ggtt(guard, cap, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1453,6 +1486,8 @@ struct Igd::Device
|
||||
|
||||
void enable_master_irq() { _mmio.enable_master_irq(); }
|
||||
|
||||
Resources &resources() { return _resources; }
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
@ -1541,6 +1576,19 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
_buffer_registry.for_each(lookup_and_free);
|
||||
}
|
||||
|
||||
void _throw_if_avail_quota_insufficient(Cap_quota caps, Ram_quota ram)
|
||||
{
|
||||
Cap_quota const c = _cap_quota_guard().avail();
|
||||
if (c.value < caps.value)
|
||||
throw Out_of_caps();
|
||||
|
||||
Ram_quota const r = _ram_quota_guard().avail();
|
||||
enum { MINIMAL_RAM_AMOUNT = 4096, };
|
||||
if (r.value < ram.value)
|
||||
throw Out_of_ram();
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -1563,7 +1611,7 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
_rm(rm),
|
||||
_ram(ram, _ram_quota_guard(), _cap_quota_guard()),
|
||||
_device(device),
|
||||
_vgpu(_device, _heap, ram, rm)
|
||||
_vgpu(_device, _heap, ram, rm, _cap_quota_guard(), _ram_quota_guard())
|
||||
{ }
|
||||
|
||||
~Session_component()
|
||||
@ -1643,7 +1691,8 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
size = ((size + 0xffful) & ~0xffful);
|
||||
|
||||
try {
|
||||
Genode::Dataspace_capability cap = _device.alloc_buffer(_heap, size);
|
||||
Genode::Dataspace_capability cap =
|
||||
_device.alloc_buffer(_heap, size, _cap_quota_guard(), _ram_quota_guard());
|
||||
|
||||
try {
|
||||
new (&_heap) Genode::Registered<Buffer>(_buffer_registry, id, cap);
|
||||
@ -1653,6 +1702,8 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
throw Gpu::Session_component::Out_of_ram();
|
||||
}
|
||||
return cap;
|
||||
} catch (Igd::Device::Out_of_caps) {
|
||||
throw Gpu::Session_component::Out_of_caps();
|
||||
} catch (Igd::Device::Out_of_ram) {
|
||||
throw Gpu::Session_component::Out_of_ram();
|
||||
}
|
||||
@ -1679,6 +1730,13 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
bool aperture,
|
||||
Gpu::Mapping_attributes attrs) override
|
||||
{
|
||||
enum {
|
||||
CAP_AMOUNT = 2,
|
||||
RAM_AMOUNT = 4096,
|
||||
};
|
||||
_throw_if_avail_quota_insufficient(Cap_quota { CAP_AMOUNT },
|
||||
Ram_quota { RAM_AMOUNT });
|
||||
|
||||
/* treat GGTT mapped buffers as rw */
|
||||
if (!(attrs.readable && attrs.writeable))
|
||||
return Genode::Dataspace_capability();
|
||||
@ -1698,8 +1756,7 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
buffer.map.cap = map.cap;
|
||||
buffer.map.offset = map.offset;
|
||||
map_cap = buffer.map.cap;
|
||||
} catch (Igd::Device::Could_not_map_buffer) {
|
||||
Genode::error("could not map buffer object");
|
||||
} catch (Ram_quota_guard::Limit_exceeded) {
|
||||
throw Gpu::Session::Out_of_ram();
|
||||
}
|
||||
};
|
||||
@ -1731,7 +1788,19 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
|
||||
bool map_buffer_ppgtt(Gpu::Buffer_id id, Gpu::addr_t va) override
|
||||
{
|
||||
enum { ALLOC_FAILED, MAP_FAILED, OK } result = ALLOC_FAILED;
|
||||
enum {
|
||||
CAP_AMOUNT = 32,
|
||||
RAM_AMOUNT = 8192,
|
||||
};
|
||||
_throw_if_avail_quota_insufficient(Cap_quota { CAP_AMOUNT },
|
||||
Ram_quota { RAM_AMOUNT });
|
||||
|
||||
enum {
|
||||
ALLOC_FAILED_RAM,
|
||||
ALLOC_FAILED_CAPS,
|
||||
MAP_FAILED,
|
||||
OK
|
||||
} result = ALLOC_FAILED_RAM;
|
||||
|
||||
auto lookup_and_map = [&] (Buffer &buffer) {
|
||||
|
||||
@ -1749,21 +1818,25 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
|
||||
buffer.ppgtt_va = va;
|
||||
buffer.ppgtt_va_valid = true;
|
||||
result = OK;
|
||||
} catch (Igd::Device::Could_not_map_buffer) {
|
||||
} catch (Igd::Device::Out_of_caps) {
|
||||
result = ALLOC_FAILED_CAPS;
|
||||
return;
|
||||
} catch (Igd::Device::Out_of_ram) {
|
||||
result = ALLOC_FAILED_RAM;
|
||||
return;
|
||||
} catch (...) {
|
||||
/* double inseration where the addresses do not match up */
|
||||
Genode::error("could not map buffer object (", Genode::Hex(va), ") into PPGTT");
|
||||
result = MAP_FAILED;
|
||||
return;
|
||||
}
|
||||
catch (Igd::Device::Out_of_ram) {
|
||||
result = ALLOC_FAILED;
|
||||
return;
|
||||
}
|
||||
};
|
||||
_apply_buffer(id, lookup_and_map);
|
||||
|
||||
switch (result) {
|
||||
case ALLOC_FAILED: throw Gpu::Session::Out_of_ram();
|
||||
case MAP_FAILED: throw Gpu::Session::Mapping_buffer_failed();
|
||||
case ALLOC_FAILED_CAPS: throw Gpu::Session::Out_of_caps();
|
||||
case ALLOC_FAILED_RAM: throw Gpu::Session::Out_of_ram();
|
||||
case MAP_FAILED: throw Gpu::Session::Mapping_buffer_failed();
|
||||
case OK: return true;
|
||||
default:
|
||||
return false;
|
||||
@ -1864,12 +1937,22 @@ class Gpu::Root : public Gpu::Root_component
|
||||
throw Session::Out_of_ram();
|
||||
}
|
||||
|
||||
Genode::Session::Resources resources = session_resources_from_args(args);
|
||||
Cap_quota const minimal_caps { 220 };
|
||||
resources.cap_quota.value -= minimal_caps.value;
|
||||
_device->resources().platform().upgrade_caps(minimal_caps.value);
|
||||
|
||||
Ram_quota const minimal_ram { 128u << 10 };
|
||||
resources.ram_quota.value -= minimal_ram.value;
|
||||
_device->resources().platform().upgrade_ram(minimal_ram.value);
|
||||
|
||||
try {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
return new (md_alloc())
|
||||
Session_component(_env.ep(), _env.ram(), _env.rm(),
|
||||
session_resources_from_args(args),
|
||||
resources,
|
||||
session_label_from_args(args),
|
||||
session_diag_from_args(args),
|
||||
*_device);
|
||||
@ -1878,8 +1961,13 @@ class Gpu::Root : public Gpu::Root_component
|
||||
|
||||
void _upgrade_session(Session_component *s, const char *args) override
|
||||
{
|
||||
s->upgrade(ram_quota_from_args(args));
|
||||
s->upgrade(cap_quota_from_args(args));
|
||||
Genode::Ram_quota ram_quota = ram_quota_from_args(args);
|
||||
ram_quota.value /= 2;
|
||||
Genode::Cap_quota caps_quota = cap_quota_from_args(args);
|
||||
caps_quota.value /= 2;
|
||||
|
||||
s->upgrade(ram_quota);
|
||||
s->upgrade(caps_quota);
|
||||
}
|
||||
|
||||
void _destroy_session(Session_component *s) override
|
||||
|
@ -273,7 +273,9 @@ class Genode::Level_4_translation_table
|
||||
Descriptor::access_t table_entry =
|
||||
Descriptor::create(flags, pa);
|
||||
|
||||
if (!Descriptor::scratch(desc, scratch->addr)) {
|
||||
/* only complain if we overmap */
|
||||
if ( !Descriptor::scratch(desc, scratch->addr)
|
||||
&& !Descriptor::scratch(desc, pa)) {
|
||||
throw Double_insertion();
|
||||
}
|
||||
desc = table_entry;
|
||||
@ -469,8 +471,7 @@ class Genode::Page_directory
|
||||
if (!alloc) { throw Allocator::Out_of_memory(); }
|
||||
|
||||
/* create and link next level table */
|
||||
try { table = new (alloc) ENTRY(scratch->next); }
|
||||
catch (...) { throw Allocator::Out_of_memory(); }
|
||||
table = new (alloc) ENTRY(scratch->next);
|
||||
ENTRY * phys_addr = (ENTRY*) alloc->phys_addr(table);
|
||||
|
||||
Gpu::addr_t const pa = (Gpu::addr_t)(phys_addr ? phys_addr : table);
|
||||
@ -661,8 +662,7 @@ class Genode::Pml4_table
|
||||
if (!alloc) { throw Allocator::Out_of_memory(); }
|
||||
|
||||
/* create and link next level table */
|
||||
try { table = new (alloc) ENTRY(scratch->next); }
|
||||
catch (...) { throw Allocator::Out_of_memory(); }
|
||||
table = new (alloc) ENTRY(scratch->next);
|
||||
|
||||
ENTRY * phys_addr = (ENTRY*) alloc->phys_addr(table);
|
||||
Gpu::addr_t const pa = (Gpu::addr_t)(phys_addr ? phys_addr : table);
|
||||
|
@ -35,11 +35,21 @@ class Igd::Ppgtt_allocator : public Genode::Translation_table_allocator
|
||||
enum { ELEMENTS = 256, };
|
||||
Utils::Address_map<ELEMENTS> _map { };
|
||||
|
||||
Genode::Cap_quota_guard &_caps_guard;
|
||||
Genode::Ram_quota_guard &_ram_guard;
|
||||
|
||||
public:
|
||||
|
||||
Ppgtt_allocator(Genode::Region_map &rm,
|
||||
Utils::Backend_alloc &backend)
|
||||
: _rm(rm), _backend(backend) { }
|
||||
Utils::Backend_alloc &backend,
|
||||
Genode::Cap_quota_guard &caps_guard,
|
||||
Genode::Ram_quota_guard &ram_guard)
|
||||
:
|
||||
_rm { rm },
|
||||
_backend { backend },
|
||||
_caps_guard { caps_guard },
|
||||
_ram_guard { ram_guard }
|
||||
{ }
|
||||
|
||||
/*************************
|
||||
** Allocator interface **
|
||||
@ -47,8 +57,8 @@ class Igd::Ppgtt_allocator : public Genode::Translation_table_allocator
|
||||
|
||||
bool alloc(size_t size, void **out_addr) override
|
||||
{
|
||||
Genode::Ram_dataspace_capability ds = _backend.alloc(size);
|
||||
if (!ds.valid()) { return false; }
|
||||
Genode::Ram_dataspace_capability ds =
|
||||
_backend.alloc(size, _caps_guard, _ram_guard);
|
||||
|
||||
*out_addr = _rm.attach(ds);
|
||||
return _map.add(ds, *out_addr);
|
||||
|
@ -30,6 +30,7 @@ namespace Utils {
|
||||
struct Backend_alloc : Genode::Interface
|
||||
{
|
||||
virtual Ram alloc(Genode::size_t) = 0;
|
||||
virtual Ram alloc(Genode::size_t, Genode::Cap_quota_guard &, Genode::Ram_quota_guard&) = 0;
|
||||
virtual void free(Ram) = 0;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user