platform_drv/x86: avoid quota leaking

Fixes #1980
This commit is contained in:
Alexander Boettcher 2016-06-01 11:08:22 +02:00 committed by Christian Helmuth
parent 470757cf15
commit c26f91ea00

View File

@ -44,8 +44,21 @@ namespace Platform {
class Rmrr; class Rmrr;
class Root; class Root;
class Ram_dataspace;
} }
class Platform::Ram_dataspace : public Genode::List<Ram_dataspace>::Element {
private:
Genode::Ram_dataspace_capability _cap;
public:
Ram_dataspace(Genode::Ram_dataspace_capability c) : _cap(c) { }
bool match(const Genode::Ram_dataspace_capability &cap) const {
return cap.local_name() == _cap.local_name(); }
};
class Platform::Rmrr : public Genode::List<Platform::Rmrr>::Element class Platform::Rmrr : public Genode::List<Platform::Rmrr>::Element
{ {
public: public:
@ -124,11 +137,15 @@ namespace Platform {
struct Resources { struct Resources {
Genode::Ram_connection _ram; Genode::Ram_connection _ram;
Genode::List<Platform::Ram_dataspace> _ram_caps;
Genode::Tslab<Platform::Ram_dataspace, 4096 - 64> _ram_caps_slab;
Resources() : Resources(Genode::Allocator_guard &md_alloc)
:
/* restrict physical address to 3G on 32bit */ /* restrict physical address to 3G on 32bit */
_ram("dma", 0, (sizeof(void *) == 4) _ram("dma", 0, (sizeof(void *) == 4)
? 0xc0000000UL : 0x100000000ULL) ? 0xc0000000UL : 0x100000000ULL),
_ram_caps_slab(&md_alloc)
{ {
/* associate _ram session with platform_drv _ram session */ /* associate _ram session with platform_drv _ram session */
_ram.ref_account(Genode::env()->ram_session_cap()); _ram.ref_account(Genode::env()->ram_session_cap());
@ -142,6 +159,18 @@ namespace Platform {
} }
Genode::Ram_connection &ram() { return _ram; } Genode::Ram_connection &ram() { return _ram; }
void insert(Genode::Ram_dataspace_capability cap) {
_ram_caps.insert(new (_ram_caps_slab) Platform::Ram_dataspace(cap)); }
bool remove(Genode::Ram_dataspace_capability cap) {
for (Platform::Ram_dataspace *ds = _ram_caps.first(); ds;
ds = ds->next())
if (ds->match(cap))
return true;
return false;
}
} _resources; } _resources;
struct Devicepd { struct Devicepd {
@ -474,6 +503,7 @@ namespace Platform {
_md_alloc(md_alloc, Genode::Arg_string::find_arg(args, "ram_quota").long_value(0)), _md_alloc(md_alloc, Genode::Arg_string::find_arg(args, "ram_quota").long_value(0)),
_device_slab(&_md_alloc), _device_slab(&_md_alloc),
_device_pd_ep(device_pd_ep), _device_pd_ep(device_pd_ep),
_resources(_md_alloc),
_label(args), _policy(_label) _label(args), _policy(_label)
{ {
/* non-pci devices */ /* non-pci devices */
@ -778,6 +808,26 @@ namespace Platform {
*/ */
typedef Genode::Ram_dataspace_capability Ram_capability; typedef Genode::Ram_dataspace_capability Ram_capability;
/**
* Helper method for rollback
*/
void _rollback(const Genode::size_t size,
const Ram_capability ram_cap = Ram_capability(),
const bool throw_oom = true)
{
if (ram_cap.valid())
_resources.ram().free(ram_cap);
if (_resources.ram().transfer_quota(Genode::env()->ram_session_cap(), size))
throw Fatal();
_md_alloc.upgrade(size);
if (throw_oom)
throw Out_of_metadata();
}
Ram_capability alloc_dma_buffer(Genode::size_t const size) Ram_capability alloc_dma_buffer(Genode::size_t const size)
{ {
if (!_device_pd.constructed()) if (!_device_pd.constructed())
@ -798,36 +848,32 @@ namespace Platform {
enum { UPGRADE_QUOTA = 4096 }; enum { UPGRADE_QUOTA = 4096 };
/* allocate dataspace from session specific ram session */ /* allocate dataspace from session specific ram session */
Ram_capability ram_cap = Genode::retry<Genode::Ram_session::Out_of_metadata>( Ram_capability ram_cap = Genode::retry<Genode::Ram_session::Quota_exceeded>(
[&] () { return _resources.ram().alloc(size, Genode::UNCACHED); },
[&] () { [&] () {
if (!_md_alloc.withdraw(UPGRADE_QUOTA)) { Ram_capability ram = Genode::retry<Genode::Ram_session::Out_of_metadata>(
/* roll-back */ [&] () { return _resources.ram().alloc(size, Genode::UNCACHED); },
if (_resources.ram().transfer_quota(Genode::env()->ram_session_cap(), size)) [&] () { throw Genode::Ram_session::Quota_exceeded(); });
throw Fatal(); return ram;
_md_alloc.upgrade(size); },
throw Out_of_metadata(); [&] () {
} if (!_md_alloc.withdraw(UPGRADE_QUOTA))
_rollback(size);
char buf[32]; char buf[32];
Genode::snprintf(buf, sizeof(buf), "ram_quota=%u", Genode::snprintf(buf, sizeof(buf), "ram_quota=%u",
UPGRADE_QUOTA); UPGRADE_QUOTA);
Genode::env()->parent()->upgrade(_resources.ram().cap(), buf); Genode::env()->parent()->upgrade(_resources.ram().cap(), buf);
}); });
if (!_device_pd->valid()) if (!ram_cap.valid())
return ram_cap; return ram_cap;
if (_device_pd->valid()) {
Genode::retry<Genode::Rm_session::Out_of_metadata>( Genode::retry<Genode::Rm_session::Out_of_metadata>(
[&] () { _device_pd->child.attach_dma_mem(ram_cap); }, [&] () { _device_pd->child.attach_dma_mem(ram_cap); },
[&] () { [&] () {
if (!_md_alloc.withdraw(UPGRADE_QUOTA)) { if (!_md_alloc.withdraw(UPGRADE_QUOTA))
/* role-back */ _rollback(size, ram_cap);
_resources.ram().free(ram_cap);
if (_resources.ram().transfer_quota(Genode::env()->ram_session_cap(), size))
throw Fatal();
_md_alloc.upgrade(size);
throw Out_of_metadata();
}
if (rs->transfer_quota(_resources.ram().cap(), UPGRADE_QUOTA)) if (rs->transfer_quota(_resources.ram().cap(), UPGRADE_QUOTA))
throw Fatal(); throw Fatal();
@ -836,23 +882,23 @@ namespace Platform {
if (_resources.ram().transfer_quota(slave.cap(), UPGRADE_QUOTA)) if (_resources.ram().transfer_quota(slave.cap(), UPGRADE_QUOTA))
throw Fatal(); throw Fatal();
}); });
}
try {
_resources.insert(ram_cap);
} catch(Genode::Allocator::Out_of_memory) {
_rollback(size, ram_cap);
}
return ram_cap; return ram_cap;
} }
void free_dma_buffer(Ram_capability ram) void free_dma_buffer(Ram_capability ram_cap)
{ {
/* if (!ram_cap.valid() || !_resources.remove(ram_cap))
* FIXME: proof that the ram cap come from us, return;
* otherwise we get bookkeeping errors
*/ Genode::size_t size = Genode::Dataspace_client(ram_cap).size();
if (ram.valid()) { _rollback(size, ram_cap, false);
Genode::size_t size = Genode::Dataspace_client(ram).size();
_resources.ram().free(ram);
if (_resources.ram().transfer_quota(Genode::env()->ram_session_cap(), size))
throw Fatal();
_md_alloc.upgrade(size);
}
} }
Device_capability device(String const &name) override; Device_capability device(String const &name) override;