From 6093f8ad81a770ba3369e4bd580d9dc3cf299955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= Date: Tue, 5 Oct 2021 17:21:46 +0200 Subject: [PATCH] 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. --- repos/libports/src/lib/libdrm/ioctl_iris.cc | 53 ++++-- repos/os/src/drivers/gpu/intel/main.cc | 164 ++++++++++++++---- repos/os/src/drivers/gpu/intel/ppgtt.h | 10 +- .../src/drivers/gpu/intel/ppgtt_allocator.h | 18 +- repos/os/src/drivers/gpu/intel/utils.h | 1 + 5 files changed, 185 insertions(+), 61 deletions(-) diff --git a/repos/libports/src/lib/libdrm/ioctl_iris.cc b/repos/libports/src/lib/libdrm/ioctl_iris.cc index 256a9eb5c0..82e59b2042 100644 --- a/repos/libports/src/lib/libdrm/ioctl_iris.cc +++ b/repos/libports/src/lib/libdrm/ioctl_iris.cc @@ -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( - [&]() { return _gpu_session.map_buffer_ppgtt(buffer.id(), - Utils::limit_to_48bit(vaddr.addr)); }, - [&]() { _gpu_session.upgrade_ram(4096); } - ); + [&]() { + return Genode::retry( + [&] () { + 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( [&] () { - buffer = - new (&_heap) Buffer(_gpu_session, size, _buffer_space); + Genode::retry( + [&] () { + 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(_env.rm().attach(b.map_cap)); - offset = b.map_offset; + Genode::retry( + [&]() { + Genode::retry( + [&] () { + b.map_cap = _gpu_session.map_buffer(b.id(), true, + Gpu::Mapping_attributes::rw()); + b.map_offset = static_cast(_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()); diff --git a/repos/os/src/drivers/gpu/intel/main.cc b/repos/os/src/drivers/gpu/intel/main.cc index 616ba48506..e93e834022 100644 --- a/repos/os/src/drivers/gpu/intel/main.cc +++ b/repos/os/src/drivers/gpu/intel/main.cc @@ -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( + [&] () { + return retry( + [&] () { + 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 _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 _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 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_registry, id, cap); @@ -1653,6 +1702,8 @@ class Gpu::Session_component : public Genode::Session_object 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 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 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 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 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 diff --git a/repos/os/src/drivers/gpu/intel/ppgtt.h b/repos/os/src/drivers/gpu/intel/ppgtt.h index 44690868db..81fb88bafc 100644 --- a/repos/os/src/drivers/gpu/intel/ppgtt.h +++ b/repos/os/src/drivers/gpu/intel/ppgtt.h @@ -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); diff --git a/repos/os/src/drivers/gpu/intel/ppgtt_allocator.h b/repos/os/src/drivers/gpu/intel/ppgtt_allocator.h index 0ef1b23cdf..d5dad045af 100644 --- a/repos/os/src/drivers/gpu/intel/ppgtt_allocator.h +++ b/repos/os/src/drivers/gpu/intel/ppgtt_allocator.h @@ -35,11 +35,21 @@ class Igd::Ppgtt_allocator : public Genode::Translation_table_allocator enum { ELEMENTS = 256, }; Utils::Address_map _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); diff --git a/repos/os/src/drivers/gpu/intel/utils.h b/repos/os/src/drivers/gpu/intel/utils.h index 4547be33b4..bf5d00d3de 100644 --- a/repos/os/src/drivers/gpu/intel/utils.h +++ b/repos/os/src/drivers/gpu/intel/utils.h @@ -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; };