platform_drv(arm): robust re-configuration support

This change of the inner working of the platform driver for ARM allows
clients to have permanent open sessions, as long as a policy node matches
the client. If devices disappear from the policy resp. from the set of
available devices (hotplug), the devices ROM of the session gets updated,
and a corresponding device session gets closed. If the device remains
untouched in the configuration but other devices appeared/disappeared, the
device session is not affected.

Ref #4330
This commit is contained in:
Stefan Kalkowski 2021-11-30 16:12:03 +01:00 committed by Norman Feske
parent 6d231597b4
commit 132e4fe815
10 changed files with 401 additions and 365 deletions

View File

@ -34,21 +34,17 @@ unsigned Driver::Rpi_device::Power_domain::id()
}; };
bool Driver::Rpi_device::acquire(Driver::Session_component & sc) void Driver::Rpi_device::acquire(Driver::Session_component & sc)
{ {
bool ret = Driver::Device::acquire(sc); Driver::Device::acquire(sc);
if (ret) { _power_domain_list.for_each([&] (Power_domain & p) {
_power_domain_list.for_each([&] (Power_domain & p) { auto & msg = sc.env().mbox.message<Property_message>();
auto & msg = sc.env().mbox.message<Property_message>(); msg.append_no_response<Property_command::Set_power_state>(p.id(),
msg.append_no_response<Property_command::Set_power_state>(p.id(), true,
true, true);
true); sc.env().mbox.call<Property_message>();
sc.env().mbox.call<Property_message>(); });
});
}
return ret;
} }

View File

@ -39,7 +39,7 @@ class Driver::Rpi_device : public Driver::Device
unsigned id(); unsigned id();
}; };
bool acquire(Session_component &) override; void acquire(Session_component &) override;
void release(Session_component &) override; void release(Session_component &) override;
Rpi_device(Device::Name name, Device::Type type) Rpi_device(Device::Name name, Device::Type type)

View File

@ -12,114 +12,29 @@
*/ */
#include <device.h> #include <device.h>
#include <device_component.h>
#include <session_component.h> #include <session_component.h>
Driver::Device::Owner::Owner(Session_component & session)
: obj_id(reinterpret_cast<void*>(&session)) {}
Driver::Device::Name Driver::Device::name() const { return _name; } Driver::Device::Name Driver::Device::name() const { return _name; }
Driver::Device::Type Driver::Device::type() const { return _type; } Driver::Device::Type Driver::Device::type() const { return _type; }
bool Driver::Device::acquire(Session_component & sc) Driver::Device::Owner Driver::Device::owner() const { return _owner; }
{
if (_session.valid() && _session != sc.label()) { return false; }
/**
* FIXME: By now, we cannot use the connection objects for IRQ and
* IOMEM and propagate missing resources when opening the
* sessions, because the combination of Genode::Env and
* Genode::Connection object transparently does quota upgrades.
* If we like to account those costs per client, we actually
* need to explicitely forward exceptions during session
* requests.
* For now, we try to measure the probable costs.
*/
Cap_quota_guard::Reservation caps(sc.cap_quota_guard(),
Cap_quota{_cap_quota_required()});
Ram_quota_guard::Reservation ram(sc.ram_quota_guard(),
Ram_quota{_ram_quota_required()});
_session = sc.label();
caps.acknowledge();
ram.acknowledge();
return true;
}
void Driver::Device::release(Session_component & sc) void Driver::Device::acquire(Session_component & sc) {
{ if (!_owner.valid()) _owner = sc; }
if (_session != sc.label()) { return; }
_io_mem_list.for_each([&] (Io_mem & io_mem) {
if (io_mem.io_mem) {
destroy(sc.heap(), io_mem.io_mem);
io_mem.io_mem = nullptr;
}
});
_irq_list.for_each([&] (Irq & irq) {
if (irq.irq) {
destroy(sc.heap(), irq.irq);
irq.irq = nullptr;
}
});
_session = Platform::Session::Label();;
sc.replenish(Cap_quota{_cap_quota_required()});
sc.replenish(Ram_quota{_ram_quota_required()});
}
Genode::Irq_session_capability Driver::Device::irq(unsigned idx, void Driver::Device::release(Session_component & sc) {
Session_component & sc) if (_owner == sc) _owner = Owner();; }
{
Irq_session_capability cap;
if (_session != sc.label()) { return cap; }
unsigned i = 0;
_irq_list.for_each([&] (Irq & irq)
{
if (i++ != idx) return;
if (!irq.irq) {
irq.irq = new (sc.heap())
Irq_connection(sc.env().env, irq.number);
}
cap = irq.irq->cap();
});
return cap;
}
Genode::Io_mem_session_capability
Driver::Device::io_mem(unsigned idx, Range &range, Cache cache, Session_component & sc)
{
Io_mem_session_capability cap;
if (_session != sc.label()) return cap;
unsigned i = 0;
_io_mem_list.for_each([&] (Io_mem & io_mem)
{
if (i++ != idx) return;
range = Range { .start = io_mem.base & 0xfff,
.size = io_mem.size };
if (!io_mem.io_mem) {
io_mem.io_mem = new (sc.heap())
Io_mem_connection(sc.env().env, io_mem.base, io_mem.size,
(cache == WRITE_COMBINED));
}
cap = io_mem.io_mem->cap();
});
return cap;
}
void Driver::Device::report(Xml_generator & xml, Session_component & sc) void Driver::Device::report(Xml_generator & xml, Session_component & sc)
@ -129,8 +44,8 @@ void Driver::Device::report(Xml_generator & xml, Session_component & sc)
xml.attribute("type", type()); xml.attribute("type", type());
_io_mem_list.for_each([&] (Io_mem & io_mem) { _io_mem_list.for_each([&] (Io_mem & io_mem) {
xml.node("io_mem", [&] () { xml.node("io_mem", [&] () {
xml.attribute("phys_addr", String<16>(Hex(io_mem.base))); xml.attribute("phys_addr", String<16>(Hex(io_mem.range.start)));
xml.attribute("size", String<16>(Hex(io_mem.size))); xml.attribute("size", String<16>(Hex(io_mem.range.size)));
}); });
}); });
_irq_list.for_each([&] (Irq & irq) { _irq_list.for_each([&] (Irq & irq) {
@ -149,36 +64,12 @@ void Driver::Device::report(Xml_generator & xml, Session_component & sc)
} }
Genode::size_t Driver::Device::_cap_quota_required()
{
/* one cap is needed for the device component itself */
size_t total = 1;
_io_mem_list.for_each([&] (Io_mem &) {
total += Io_mem_session::CAP_QUOTA; });
_irq_list.for_each([&] (Irq &) {
total += Irq_session::CAP_QUOTA; });
return total;
}
Genode::size_t Driver::Device::_ram_quota_required()
{
size_t total = 0;
_io_mem_list.for_each([&] (Io_mem &) {
total += Io_mem_session::RAM_QUOTA; });
_irq_list.for_each([&] (Irq &) {
total += Irq_session::RAM_QUOTA; });
return total;
}
Driver::Device::Device(Name name, Type type) Driver::Device::Device(Name name, Type type)
: _name(name), _type(type) { } : _name(name), _type(type) { }
Driver::Device::~Device() Driver::Device::~Device()
{ {
if (_session.valid()) { if (_owner.valid()) {
error("Device to be destroyed, still obtained by session ", error("Device to be destroyed, still obtained by session"); }
_session); }
} }

View File

@ -15,8 +15,6 @@
#define _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_H_ #define _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_H_
#include <base/allocator.h> #include <base/allocator.h>
#include <io_mem_session/connection.h>
#include <irq_session/connection.h>
#include <platform_session/device.h> #include <platform_session/device.h>
#include <util/list.h> #include <util/list.h>
#include <util/list_model.h> #include <util/list_model.h>
@ -41,20 +39,34 @@ class Driver::Device : private List_model<Device>::Element
{ {
public: public:
using Name = Genode::String<64>;
using Type = Genode::String<64>;
using Range = Platform::Device_interface::Range;
struct Owner
{
void * obj_id;
Owner() : obj_id(nullptr) {}
Owner(Session_component & session);
bool operator == (Owner const & o) const {
return obj_id == o.obj_id; }
bool valid() const {
return obj_id != nullptr; }
};
struct Io_mem : List_model<Io_mem>::Element struct Io_mem : List_model<Io_mem>::Element
{ {
addr_t base; Range range;
size_t size;
Io_mem_connection * io_mem { nullptr };
Io_mem(addr_t base, size_t size) Io_mem(Range range) : range(range) {}
: base(base), size(size) {}
}; };
struct Irq : List_model<Irq>::Element struct Irq : List_model<Irq>::Element
{ {
unsigned number; unsigned number;
Irq_connection * irq { nullptr };
Irq(unsigned number) : number(number) {} Irq(unsigned number) : number(number) {}
}; };
@ -71,22 +83,29 @@ class Driver::Device : private List_model<Device>::Element
: name(name), value(value) {} : name(name), value(value) {}
}; };
using Name = Genode::String<64>;
using Type = Genode::String<64>;
using Range = Platform::Device_interface::Range;
Device(Name name, Type type); Device(Name name, Type type);
virtual ~Device(); virtual ~Device();
Name name() const; Name name() const;
Type type() const; Type type() const;
Owner owner() const;
virtual bool acquire(Session_component &); virtual void acquire(Session_component &);
virtual void release(Session_component &); virtual void release(Session_component &);
Irq_session_capability irq(unsigned idx, Session_component &); template <typename FN> void for_each_irq(FN const & fn)
Io_mem_session_capability io_mem(unsigned idx, Range &, Cache, {
Session_component &); unsigned idx = 0;
_irq_list.for_each([&] (Irq const & irq) {
fn(idx++, irq.number); });
}
template <typename FN> void for_each_io_mem(FN const & fn)
{
unsigned idx = 0;
_io_mem_list.for_each([&] (Io_mem const & iomem) {
fn(idx++, iomem.range); });
}
void report(Xml_generator &, Session_component &); void report(Xml_generator &, Session_component &);
@ -95,19 +114,16 @@ class Driver::Device : private List_model<Device>::Element
virtual void _report_platform_specifics(Xml_generator &, virtual void _report_platform_specifics(Xml_generator &,
Session_component &) {} Session_component &) {}
size_t _cap_quota_required();
size_t _ram_quota_required();
friend class Driver::Device_model; friend class Driver::Device_model;
friend class List_model<Device>; friend class List_model<Device>;
friend class List<Device>; friend class List<Device>;
Name _name; Name _name;
Type _type; Type _type;
Platform::Session::Label _session {}; Owner _owner {};
List_model<Io_mem> _io_mem_list {}; List_model<Io_mem> _io_mem_list {};
List_model<Irq> _irq_list {}; List_model<Irq> _irq_list {};
List_model<Property> _property_list {}; List_model<Property> _property_list {};
/* /*
* Noncopyable * Noncopyable
@ -201,18 +217,18 @@ struct Driver::Io_mem_update_policy : Genode::List_model<Device::Io_mem>::Update
Element & create_element(Genode::Xml_node node) Element & create_element(Genode::Xml_node node)
{ {
Genode::addr_t base = node.attribute_value<Genode::addr_t>("address", 0); Device::Range range { node.attribute_value<Genode::addr_t>("address", 0),
Genode::size_t size = node.attribute_value<Genode::size_t>("size", 0); node.attribute_value<Genode::size_t>("size", 0) };
return *(new (alloc) Element(base, size)); return *(new (alloc) Element(range));
} }
void update_element(Element &, Genode::Xml_node) {} void update_element(Element &, Genode::Xml_node) {}
static bool element_matches_xml_node(Element const & iomem, Genode::Xml_node node) static bool element_matches_xml_node(Element const & iomem, Genode::Xml_node node)
{ {
Genode::addr_t base = node.attribute_value<Genode::addr_t>("address", 0); Device::Range range { node.attribute_value<Genode::addr_t>("address", 0),
Genode::size_t size = node.attribute_value<Genode::size_t>("size", 0); node.attribute_value<Genode::size_t>("size", 0) };
return (base == iomem.base) && (size == iomem.size); return (range.start == iomem.range.start) && (range.size == iomem.range.size);
} }
static bool node_is_element(Genode::Xml_node node) static bool node_is_element(Genode::Xml_node node)

View File

@ -17,36 +17,47 @@
using Driver::Device_component; using Driver::Device_component;
void Driver::Device_component::_release_resources()
{
_io_mem_registry.for_each([&] (Io_mem & iomem) {
destroy(_session.heap(), &iomem); });
_irq_registry.for_each([&] (Irq & irq) {
destroy(_session.heap(), &irq); });
_session.ram_quota_guard().replenish(Ram_quota{_ram_quota});
_session.cap_quota_guard().replenish(Cap_quota{_cap_quota});
}
Driver::Device::Name Device_component::device() const { return _device; } Driver::Device::Name Device_component::device() const { return _device; }
Driver::Session_component & Device_component::session() { return _session; } Driver::Session_component & Device_component::session() { return _session; }
bool Driver::Device_component::acquire()
{
bool acquired = false;
_session.env().devices.for_each([&] (Driver::Device & device) {
if (device.name() == _device) {
acquired = device.acquire(_session); }});
return acquired;
}
void Driver::Device_component::release()
{
_session.env().devices.for_each([&] (Driver::Device & device) {
if (device.name() == _device) { device.release(_session); }});
}
Genode::Io_mem_session_capability Genode::Io_mem_session_capability
Device_component::io_mem(unsigned idx, Range &range, Cache cache) Device_component::io_mem(unsigned idx, Range &range, Cache cache)
{ {
Io_mem_session_capability cap; Io_mem_session_capability cap;
_session.env().devices.for_each([&] (Driver::Device & device) {
if (device.name() == _device) { _io_mem_registry.for_each([&] (Io_mem & iomem)
cap = device.io_mem(idx, range, cache, _session); }}); {
if (iomem.idx != idx)
return;
if (!iomem.io_mem.constructed())
iomem.io_mem.construct(_session.env().env,
iomem.range.start,
iomem.range.size,
cache == WRITE_COMBINED);
range = iomem.range;
range.start &= 0xfff;
cap = iomem.io_mem->cap();
});
return cap; return cap;
} }
@ -54,22 +65,64 @@ Device_component::io_mem(unsigned idx, Range &range, Cache cache)
Genode::Irq_session_capability Device_component::irq(unsigned idx) Genode::Irq_session_capability Device_component::irq(unsigned idx)
{ {
Irq_session_capability cap; Irq_session_capability cap;
_session.env().devices.for_each([&] (Driver::Device & device) {
if (device.name() == _device) { cap = device.irq(idx, _session); }}); _irq_registry.for_each([&] (Irq & irq)
{
if (irq.idx != idx)
return;
if (!irq.irq.constructed())
irq.irq.construct(_session.env().env, irq.number);
cap = irq.irq->cap();
});
return cap; return cap;
} }
void Driver::Device_component::report(Xml_generator & xml) Device_component::Device_component(Registry<Device_component> & registry,
Driver::Session_component & session,
Driver::Device & device)
: _session(session), _device(device.name()), _reg_elem(registry, *this)
{ {
_session.env().devices.for_each([&] (Driver::Device & device) { session.cap_quota_guard().withdraw(Cap_quota{1});
if (device.name() == _device) { device.report(xml, _session); }}); _cap_quota += 1;
/**
* FIXME: By now, we cannot use the connection objects for IRQ and
* IOMEM and propagate missing resources when opening the
* sessions, because the combination of Genode::Env and
* Genode::Connection object transparently does quota upgrades.
* If we like to account those costs per client, we actually
* need to explicitely forward exceptions during session
* requests.
* For now, we try to measure the probable costs.
*/
try {
device.for_each_irq([&] (unsigned idx, unsigned nr)
{
session.ram_quota_guard().withdraw(Ram_quota{Irq_session::RAM_QUOTA});
_ram_quota += Irq_session::RAM_QUOTA;
session.cap_quota_guard().withdraw(Cap_quota{Irq_session::CAP_QUOTA});
_cap_quota += Irq_session::CAP_QUOTA;
new (session.heap()) Irq(_irq_registry, idx, nr);
});
device.for_each_io_mem([&] (unsigned idx, Range range)
{
session.ram_quota_guard().withdraw(Ram_quota{Io_mem_session::RAM_QUOTA});
_ram_quota += Io_mem_session::RAM_QUOTA;
session.cap_quota_guard().withdraw(Cap_quota{Io_mem_session::CAP_QUOTA});
_cap_quota += Io_mem_session::CAP_QUOTA;
new (session.heap()) Io_mem(_io_mem_registry, idx, range);
});
} catch(...) {
_release_resources();
throw;
}
} }
Device_component::Device_component(Driver::Session_component & session, Device_component::~Device_component() { _release_resources(); };
Driver::Device::Name const device)
: _session(session), _device(device) { }
Device_component::~Device_component() { release(); }

View File

@ -14,9 +14,13 @@
#ifndef _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_COMPONENT_H_ #ifndef _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_COMPONENT_H_
#define _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_COMPONENT_H_ #define _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_COMPONENT_H_
#include <base/registry.h>
#include <base/rpc_server.h> #include <base/rpc_server.h>
#include <platform_session/platform_session.h> #include <io_mem_session/connection.h>
#include <irq_session/connection.h>
#include <platform_session/device.h> #include <platform_session/device.h>
#include <platform_session/platform_session.h>
#include <util/reconstructible.h>
#include <env.h> #include <env.h>
#include <device.h> #include <device.h>
@ -32,18 +36,42 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
{ {
public: public:
Device_component(Session_component & session, struct Irq : Registry<Irq>::Element
Driver::Device::Name const device); {
unsigned idx;
unsigned number;
Constructible<Irq_connection> irq {};
Irq(Registry<Irq> & registry,
unsigned idx,
unsigned number)
:
Registry<Irq>::Element(registry, *this),
idx(idx), number(number) {}
};
struct Io_mem : Registry<Io_mem>::Element
{
unsigned idx;
Range range;
Constructible<Io_mem_connection> io_mem {};
Io_mem(Registry<Io_mem> & registry,
unsigned idx,
Range range)
:
Registry<Io_mem>::Element(registry, *this),
idx(idx), range(range) {}
};
Device_component(Registry<Device_component> & registry,
Session_component & session,
Driver::Device & device);
~Device_component(); ~Device_component();
Driver::Device::Name device() const; Driver::Device::Name device() const;
Session_component & session(); Session_component & session();
bool acquire();
void release();
void report(Xml_generator&);
/************************************ /************************************
** Platform::Device RPC functions ** ** Platform::Device RPC functions **
@ -54,12 +82,15 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
private: private:
friend class Session_component; Session_component & _session;
Driver::Device::Name const _device;
size_t _cap_quota { 0 };
size_t _ram_quota { 0 };
Registry<Device_component>::Element _reg_elem;
Registry<Irq> _irq_registry {};
Registry<Io_mem> _io_mem_registry {};
Session_component & _session; void _release_resources();
Driver::Device::Name const _device;
Capability<Platform::Device> _cap {};
List_element<Device_component> _list_elem { this };
/* /*
* Noncopyable * Noncopyable

View File

@ -39,15 +39,8 @@ struct Driver::Main
void Driver::Main::update_config() void Driver::Main::update_config()
{ {
env.config.update(); env.config.update();
/**
* We must perform the policy update before updating the devices since
* the former may need to release devices and its corresponding Io_mem
* and Irq connections when closing a session that is no longer supposed
* to exist. For doing so, it must access the old device model.
*/
root.update_policy();
env.devices.update(env.config.xml()); env.devices.update(env.config.xml());
root.update_policy();
} }
void Component::construct(Genode::Env &env) { void Component::construct(Genode::Env &env) {

View File

@ -13,32 +13,22 @@
#include <root.h> #include <root.h>
using Version = Driver::Session_component::Policy_version;
void Driver::Root::update_policy() void Driver::Root::update_policy()
{ {
_sessions.for_each([&] (Session_component & sc) { _sessions.for_each([&] (Session_component & sc)
{
bool policy_changed = false;
unsigned device_count = 0;
try { try {
Session_policy const policy { sc._label, _env.config.xml() }; Session_policy const policy { sc._label, _env.config.xml() };
sc.update_policy(policy.attribute_value("info", false),
policy.for_each_sub_node("device", [&] (Xml_node node) { policy.attribute_value("version", Version()));
device_count++;
if (!sc.has_device(node.attribute_value("name",
Device::Name()))) {
policy_changed = true; }
});
if (device_count != sc.devices_count()) { policy_changed = true; }
} }
catch (Session_policy::No_policy_defined) { catch (Session_policy::No_policy_defined) {
policy_changed = true;
error("No matching policy for '", sc._label.string(), error("No matching policy for '", sc._label.string(),
"' anymore, will close the session!"); "' anymore, will close the session!");
close(sc.cap());
} }
if (policy_changed) { close(sc.cap()); }
}); });
} }
@ -55,10 +45,8 @@ Driver::Session_component * Driver::Root::_create_session(const char *args)
Session_component(_env, _sessions, label, Session_component(_env, _sessions, label,
session_resources_from_args(args), session_resources_from_args(args),
session_diag_from_args(args), session_diag_from_args(args),
policy.attribute_value("info", false)); policy.attribute_value("info", false),
policy.attribute_value("version", Version()));
policy.for_each_sub_node("device", [&] (Xml_node node) {
sc->add(node.attribute_value("name", Driver::Device::Name())); });
} catch (Session_policy::No_policy_defined) { } catch (Session_policy::No_policy_defined) {
error("Invalid session request, no matching policy for ", error("Invalid session request, no matching policy for ",
"'", label_from_args(args).string(), "'"); "'", label_from_args(args).string(), "'");

View File

@ -18,11 +18,93 @@
using Driver::Session_component; using Driver::Session_component;
Genode::Capability<Platform::Device_interface>
Session_component::_acquire(Device & device)
{
Device_component * dc = new (heap())
Device_component(_device_registry, *this, device);
device.acquire(*this);
return _env.env.ep().rpc_ep().manage(dc);
};
void Session_component::_release_device(Device_component & dc)
{
Device::Name name = dc.device();
_env.env.ep().rpc_ep().dissolve(&dc);
destroy(heap(), &dc);
_env.devices.for_each([&] (Device & dev) {
if (name == dev.name()) dev.release(*this); });
}
void Session_component::_free_dma_buffer(Dma_buffer & buf)
{
Ram_dataspace_capability cap = buf.cap;
destroy(heap(), &buf);
_env_ram.free(cap);
}
bool Session_component::matches(Device & dev) const
{
bool ret = false;
try {
Session_policy const policy { label(), _env.config.xml() };
policy.for_each_sub_node("device", [&] (Xml_node node) {
if (dev.name() == node.attribute_value("name", Device::Name()))
ret = true;
});
} catch (Session_policy::No_policy_defined) { }
return ret;
};
void Session_component::update_policy(bool info, Policy_version version)
{
_info = info;
_version = version;
enum Device_state { AWAY, CHANGED, UNCHANGED };
_device_registry.for_each([&] (Device_component & dc) {
Device_state state = AWAY;
_env.devices.for_each([&] (Device & dev) {
if (dev.name() != dc.device())
return;
state = (dev.owner() == _owner_id) ? UNCHANGED : CHANGED;
});
if (state == UNCHANGED)
return;
if (state == AWAY)
warning("Device ", dc.device(),
" has changed, will close device session");
else
warning("Device ", dc.device(),
" unavailable, will close device session");
_release_device(dc);
});
update_devices_rom();
};
void Session_component::produce_xml(Xml_generator &xml) void Session_component::produce_xml(Xml_generator &xml)
{ {
if (!_info) { return; } if (!_info)
for (Device_list_element * e = _device_list.first(); e; e = e->next()) { return;
e->object()->report(xml); }
if (_version.valid())
xml.attribute("version", _version);
_env.devices.for_each([&] (Device & dev) {
if (matches(dev)) dev.report(xml, *this); });
} }
@ -32,39 +114,6 @@ Genode::Heap & Session_component::heap() { return _md_alloc; }
Driver::Env & Session_component::env() { return _env; } Driver::Env & Session_component::env() { return _env; }
void Session_component::add(Device::Name const & device)
{
if (has_device(device)) return;
Device_component * new_dc =
new (_md_alloc) Device_component(*this, device);
Device_list_element * last = nullptr;
for (last = _device_list.first(); last; last = last->next()) {
if (!last->next()) break; }
_device_list.insert(&new_dc->_list_elem, last);
}
bool Session_component::has_device(Device::Name const & device) const
{
for (Device_list_element const * e = _device_list.first(); e;
e = e->next()) {
if (e->object()->device() == device) { return true; }
}
return false;
}
unsigned Session_component::devices_count() const
{
unsigned counter = 0;
for (Device_list_element const * e = _device_list.first();
e; e = e->next()) { counter++; }
return counter;
}
void Session_component::update_devices_rom() void Session_component::update_devices_rom()
{ {
_rom_session.trigger_update(); _rom_session.trigger_update();
@ -78,40 +127,59 @@ Genode::Rom_session_capability Session_component::devices_rom() {
Genode::Capability<Platform::Device_interface> Genode::Capability<Platform::Device_interface>
Session_component::acquire_device(Platform::Session::Device_name const &name) Session_component::acquire_device(Platform::Session::Device_name const &name)
{ {
for (Device_list_element * e = _device_list.first(); e; e = e->next()) { Capability<Platform::Device_interface> cap;
if (e->object()->device() != name.string()) { continue; }
if (!e->object()->acquire()) { /* Search for existing, aquired device session */
error("Device ", e->object()->device(), _device_registry.for_each([&] (Device_component & dc) {
" already acquired!"); if (dc.device() == name)
break; cap = dc.cap();
} });
return _env.env.ep().rpc_ep().manage(e->object()); if (cap.valid())
} return cap;
return Capability<Platform::Device_interface>(); _env.devices.for_each([&] (Device & dev)
{
if (dev.name() != name || !matches(dev))
return;
if (dev.owner().valid())
warning("Cannot aquire device ", name, " already in use");
else
cap = _acquire(dev);
});
return cap;
} }
Genode::Capability<Platform::Device_interface> Genode::Capability<Platform::Device_interface>
Session_component::acquire_single_device() Session_component::acquire_single_device()
{ {
Device_list_element * e = _device_list.first(); Capability<Platform::Device_interface> cap;
if (!e) { return Capability<Platform::Device_interface>(); }
return acquire_device(e->object()->device()); /* Search for existing, aquired device session */
_device_registry.for_each([&] (Device_component & dc) {
cap = dc.cap(); });
if (cap.valid())
return cap;
_env.devices.for_each([&] (Device & dev) {
if (matches(dev) && !dev.owner().valid())
cap = _acquire(dev); });
return cap;
} }
void Session_component::release_device(Capability<Platform::Device_interface> device_cap) void Session_component::release_device(Capability<Platform::Device_interface> device_cap)
{ {
_env.env.ep().rpc_ep().apply(device_cap, [&] (Device_component * dc) { if (!device_cap.valid())
if (!dc) return;
return;
_env.env.ep().rpc_ep().dissolve(dc); _device_registry.for_each([&] (Device_component & dc) {
dc->release(); if (device_cap.local_name() == dc.cap().local_name())
}); _release_device(dc); });
} }
@ -123,7 +191,7 @@ Session_component::alloc_dma_buffer(size_t const size, Cache cache)
if (!ram_cap.valid()) return ram_cap; if (!ram_cap.valid()) return ram_cap;
try { try {
_buffer_list.insert(new (_md_alloc) Dma_buffer(ram_cap)); new (heap()) Dma_buffer(_buffer_registry, ram_cap);
} catch (Out_of_ram) { } catch (Out_of_ram) {
_env_ram.free(ram_cap); _env_ram.free(ram_cap);
throw; throw;
@ -140,44 +208,41 @@ void Session_component::free_dma_buffer(Ram_dataspace_capability ram_cap)
{ {
if (!ram_cap.valid()) { return; } if (!ram_cap.valid()) { return; }
for (Dma_buffer * buf = _buffer_list.first(); buf; buf = buf->next()) { _buffer_registry.for_each([&] (Dma_buffer & buf) {
if (buf.cap.local_name() == ram_cap.local_name())
if (buf->cap.local_name() != ram_cap.local_name()) continue; _free_dma_buffer(buf); });
_buffer_list.remove(buf);
destroy(_md_alloc, buf);
_env_ram.free(ram_cap);
return;
}
} }
Genode::addr_t Session_component::dma_addr(Ram_dataspace_capability ram_cap) Genode::addr_t Session_component::dma_addr(Ram_dataspace_capability ram_cap)
{ {
if (!ram_cap.valid()) { return 0; } addr_t ret = 0;
for (Dma_buffer * buf = _buffer_list.first(); buf; buf = buf->next()) { if (!ram_cap.valid())
return ret;
if (buf->cap.local_name() != ram_cap.local_name()) continue; _buffer_registry.for_each([&] (Dma_buffer & buf) {
if (buf.cap.local_name() == ram_cap.local_name()) {
Dataspace_client dsc(buf.cap);
ret = dsc.phys_addr();
} });
Dataspace_client dsc(buf->cap); return ret;
return dsc.phys_addr();
}
return 0;
} }
Session_component::Session_component(Driver::Env & env, Session_component::Session_component(Driver::Env & env,
Session_registry & registry, Session_registry & registry,
Label const & label, Label const & label,
Resources const & resources, Resources const & resources,
Diag const & diag, Diag const & diag,
bool const info) bool const info,
: Session_object<Platform::Session>(env.env.ep(), resources, label, diag), Policy_version const version)
Session_registry::Element(registry, *this), :
Dynamic_rom_session::Xml_producer("devices"), Session_object<Platform::Session>(env.env.ep(), resources, label, diag),
_env(env), _info(info) Session_registry::Element(registry, *this),
Dynamic_rom_session::Xml_producer("devices"),
_env(env), _info(info), _version(version)
{ {
/* /*
* FIXME: As the ROM session does not propagate Out_of_* * FIXME: As the ROM session does not propagate Out_of_*
@ -188,25 +253,21 @@ Session_component::Session_component(Driver::Env & env,
* we account the costs here until the ROM session interface * we account the costs here until the ROM session interface
* changes. * changes.
*/ */
_cap_quota_guard().withdraw(Cap_quota{1}); _cap_quota_guard().withdraw(Cap_quota{Rom_session::CAP_QUOTA});
_ram_quota_guard().withdraw(Ram_quota{5*1024}); _ram_quota_guard().withdraw(Ram_quota{5*1024});
} }
Session_component::~Session_component() Session_component::~Session_component()
{ {
while (_device_list.first()) { _device_registry.for_each([&] (Device_component & dc) {
Device_list_element * e = _device_list.first(); _release_device(dc); });
release_device(e->object()->cap());
_device_list.remove(e);
destroy(_md_alloc, e->object());
}
/* also free up dma buffers */ /* free up dma buffers */
while (_buffer_list.first()) _buffer_registry.for_each([&] (Dma_buffer & buf) {
free_dma_buffer(_buffer_list.first()->cap); _free_dma_buffer(buf); });
/* replenish quota for rom sessions, see constructor for explanation */ /* replenish quota for rom sessions, see constructor for explanation */
_cap_quota_guard().replenish(Cap_quota{1}); _cap_quota_guard().replenish(Cap_quota{Rom_session::CAP_QUOTA});
_ram_quota_guard().replenish(Ram_quota{5*1024}); _ram_quota_guard().replenish(Ram_quota{5*1024});
} }

View File

@ -40,26 +40,29 @@ class Driver::Session_component
public: public:
using Session_registry = Registry<Session_component>; using Session_registry = Registry<Session_component>;
using Policy_version = String<64>;
Session_component(Driver::Env & env,
Session_registry & registry,
Label const & label,
Resources const & resources,
Diag const & diag,
bool const info,
Policy_version const version);
Session_component(Driver::Env & env,
Session_registry & registry,
Label const & label,
Resources const & resources,
Diag const & diag,
bool const info);
~Session_component(); ~Session_component();
Heap & heap(); Heap & heap();
Driver::Env & env(); Driver::Env & env();
void add(Device::Name const &); bool matches(Device &) const;
bool has_device(Device::Name const &) const; void update_devices_rom();
unsigned devices_count() const;
void update_devices_rom();
Ram_quota_guard & ram_quota_guard() { return _ram_quota_guard(); } Ram_quota_guard & ram_quota_guard() { return _ram_quota_guard(); }
Cap_quota_guard & cap_quota_guard() { return _cap_quota_guard(); } Cap_quota_guard & cap_quota_guard() { return _cap_quota_guard(); }
void update_policy(bool info, Policy_version version);
/************************** /**************************
** Platform Session API ** ** Platform Session API **
@ -80,28 +83,32 @@ class Driver::Session_component
friend class Root; friend class Root;
struct Dma_buffer : List<Dma_buffer>::Element struct Dma_buffer : Registry<Dma_buffer>::Element
{ {
Ram_dataspace_capability const cap; Ram_dataspace_capability const cap;
Dma_buffer(Ram_dataspace_capability const cap) Dma_buffer(Registry<Dma_buffer> & registry,
: cap(cap) {} Ram_dataspace_capability const cap)
: Registry<Dma_buffer>::Element(registry, *this), cap(cap) {}
}; };
using Device_list_element = List_element<Device_component>; Driver::Env & _env;
using Device_list = List<Device_list_element>; Device::Owner _owner_id { *this };
Constrained_ram_allocator _env_ram { _env.env.pd(),
Driver::Env & _env; _ram_quota_guard(),
Constrained_ram_allocator _env_ram { _env.env.pd(), _cap_quota_guard() };
_ram_quota_guard(), Heap _md_alloc { _env_ram, _env.env.rm() };
_cap_quota_guard() }; Registry<Device_component> _device_registry { };
Heap _md_alloc { _env_ram, _env.env.rm() }; Registry<Dma_buffer> _buffer_registry { };
Device_list _device_list { }; Dynamic_rom_session _rom_session { _env.env.ep(), _env.env.ram(),
List<Dma_buffer> _buffer_list { }; _env.env.rm(), *this };
Dynamic_rom_session _rom_session { _env.env.ep(), _env.env.ram(), bool _info;
_env.env.rm(), *this }; Policy_version _version;
bool const _info;
Device_capability _acquire(Device & device);
void _release_device(Device_component & dc);
void _free_dma_buffer(Dma_buffer & buf);
/* /*
* Noncopyable * Noncopyable
*/ */