acpica: retrieve static battery info only once

On Fujitsu S936, evaluating the _BIF method on each battery-info update
successively increased the RAM usage, while  _BST (dynamic battery
status) and _STA (generic status) did not show this behavior. Therefore
this commit retrieves only dynamic information periodically (resp. on
SCI IRQ). Now, acpica hast static RAM usage in idle state for 24+ hours.

The root cause for the increased RAM usage is still shady. While it
could just be normal that it grows until a certain yet unknown limit,
there may also be memory leak in contrib code or some strange AML on the
designated notebook.

Issue #3454
This commit is contained in:
Christian Helmuth 2019-07-18 14:29:18 +02:00
parent a7835650e8
commit ce149397ec

View File

@ -11,35 +11,166 @@
* under the terms of the GNU Affero General Public License version 3.
*/
class Battery : Acpica::Callback<Battery> {
class Battery : Acpica::Callback<Battery>
{
private:
Acpica::Reportstate * _report;
ACPI_HANDLE _sb;
/*
* ACPI spec - 10.2.2.1 _BIF (Battery Information)
* (alternatively 10.2.2.2 _BIX could be used)
*/
Acpica::Buffer<char [512]> _battery;
Genode::String<16> _battery_name;
void _init_static_info()
{
ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BIF"),
nullptr, &_battery,
ACPI_TYPE_PACKAGE);
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(_battery.object);
if (ACPI_FAILURE(res) || !obj || obj->Package.Count != 13) {
Genode::error("failed - '", __func__, "' _BIF res=", Genode::Hex(res));
return;
}
Acpica::Buffer<char [8]> battery_name;
res = AcpiGetName(_sb, ACPI_SINGLE_NAME, &battery_name);
if (ACPI_FAILURE(res)) {
_battery_name = Genode::String<16>("unknown");
} else {
_battery_name = Genode::String<16>(battery_name.object);
}
}
void _info(Genode::Xml_generator &xml)
{
xml.node("name", [&] { xml.append(_battery_name.string()); });
const char * node_name[] = {
"powerunit", "design_capacity", "last_full_capacity",
"technology", "voltage", "warning_capacity", "low_capacity",
"granularity1", "granularity2", "serial", "model", "type",
"oem"
};
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(_battery.object);
if (sizeof(node_name) / sizeof(node_name[0]) != obj->Package.Count)
return;
for (unsigned i = 0; i < 9; i++) {
ACPI_OBJECT * v = &obj->Package.Elements[i];
xml.node(node_name[i], [&] {
if (v->Type != ACPI_TYPE_INTEGER) {
xml.append("unknown");
return;
}
xml.attribute("value", v->Integer.Value);
if (i == 0)
xml.append(v->Integer.Value == 0 ? "mW/mWh" :
v->Integer.Value == 1 ? "mA/mAh" :
"unknown");
if (i == 3)
xml.append(v->Integer.Value == 0 ? "primary" :
v->Integer.Value == 1 ? "secondary" :
"unknown");
});
}
for (unsigned i = 9; i < obj->Package.Count; i++) {
ACPI_OBJECT * v = &obj->Package.Elements[i];
xml.node(node_name[i], [&] {
if (v->Type != ACPI_TYPE_STRING)
return;
xml.append(v->String.Pointer);
});
}
}
void _status(Genode::Xml_generator &xml)
{
/* 10.2.2.6 _BST (Battery Status) */
Acpica::Buffer<char [256]> dynamic;
ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BST"),
nullptr, &dynamic,
ACPI_TYPE_PACKAGE);
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(dynamic.object);
if (ACPI_FAILURE(res) || !obj ||
obj->Package.Count != 4) {
Genode::error("failed - '", __func__, "' _BST res=", Genode::Hex(res));
return;
}
Acpica::Buffer<ACPI_OBJECT> sta;
res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_STA"), nullptr,
&sta, ACPI_TYPE_INTEGER);
if (ACPI_FAILURE(res)) {
xml.node("status", [&] { xml.append("unknown"); });
} else
xml.node("status", [&] {
xml.attribute("value", sta.object.Integer.Value);
/* see "6.3.7 _STA" for more human readable decoding */
if (!(sta.object.Integer.Value & ACPI_STA_BATTERY_PRESENT))
xml.append("battery not present");
});
const char * node_name[] = {
"state", "present_rate", "remaining_capacity",
"present_voltage"
};
if (sizeof(node_name) / sizeof(node_name[0]) != obj->Package.Count)
return;
for (unsigned i = 0; i < obj->Package.Count; i++) {
ACPI_OBJECT * v = &obj->Package.Elements[i];
xml.node(node_name[i], [&] {
if (v->Type != ACPI_TYPE_INTEGER) {
xml.append("unknown");
return;
}
xml.attribute("value", v->Integer.Value);
if (i != 0)
return;
if (v->Integer.Value & 0x1) xml.append("discharging");
if (v->Integer.Value & 0x2) xml.append("charging");
if (v->Integer.Value & 0x4) xml.append("critical low");
});
}
}
public:
Battery(void * report, ACPI_HANDLE sb)
: _report(reinterpret_cast<Acpica::Reportstate *>(report)), _sb(sb)
{
_init_static_info();
if (_report)
_report->add_notify(this);
}
void handle(ACPI_HANDLE sb, UINT32 value)
{
if (_report)
_report->battery_event();
}
static ACPI_STATUS detect(ACPI_HANDLE sb, UINT32, void *m, void **)
{
Acpica::Main * main = reinterpret_cast<Acpica::Main *>(m);
Battery * dev_obj = new (main->heap) Battery(main->report, sb);
ACPI_STATUS res = AcpiInstallNotifyHandler (sb, ACPI_DEVICE_NOTIFY,
handler, dev_obj);
ACPI_STATUS res =
AcpiInstallNotifyHandler(sb, ACPI_DEVICE_NOTIFY,
Acpica::Callback<Battery>::handler,
dev_obj);
if (ACPI_FAILURE(res)) {
Genode::error("failed - '", __func__, "' "
"res=", Genode::Hex(res));
@ -48,7 +179,7 @@ class Battery : Acpica::Callback<Battery> {
}
Acpica::Buffer<char [8]> battery_name;
AcpiGetName (sb, ACPI_SINGLE_NAME, &battery_name);
res = AcpiGetName (sb, ACPI_SINGLE_NAME, &battery_name);
if (ACPI_FAILURE(res)) {
Genode::error("failed - '", __func__, "' battery name "
"res=", Genode::Hex(res));
@ -106,130 +237,19 @@ class Battery : Acpica::Callback<Battery> {
return AE_OK;
}
/*
* Acpica::Callback<> interface
*/
void handle(ACPI_HANDLE sb, UINT32 value)
{
if (_report)
_report->battery_event();
}
void generate(Genode::Xml_generator &xml)
{
info(xml);
status(xml);
}
void info(Genode::Xml_generator &xml)
{
/* ACPI spec - 10.2.2.1 _BIF (Battery Information) */
Acpica::Buffer<char [512]> battery;
ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BIF"),
nullptr, &battery,
ACPI_TYPE_PACKAGE);
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(battery.object);
if (ACPI_FAILURE(res) || !obj || obj->Package.Count != 13) {
Genode::error("failed - '", __func__, "' _BIF res=", Genode::Hex(res));
return;
}
Acpica::Buffer<char [8]> battery_name;
AcpiGetName (_sb, ACPI_SINGLE_NAME, &battery_name);
if (ACPI_FAILURE(res))
xml.node("name", [&] { xml.append("unknown"); });
else
xml.node("name", [&] { xml.append(battery_name.object); });
const char * node_name[] = {
"powerunit", "design_capacity", "last_full_capacity",
"technology", "voltage", "warning_capacity", "low_capacity",
"granularity1", "granularity2", "serial", "model", "type",
"oem"
};
if (sizeof(node_name) / sizeof(node_name[0]) != obj->Package.Count)
return;
for (unsigned i = 0; i < 9; i++) {
ACPI_OBJECT * v = &obj->Package.Elements[i];
xml.node(node_name[i], [&] {
if (v->Type != ACPI_TYPE_INTEGER) {
xml.append("unknown");
return;
}
xml.attribute("value", v->Integer.Value);
if (i == 0)
xml.append(v->Integer.Value == 0 ? "mW/mWh" :
v->Integer.Value == 1 ? "mA/mAh" :
"unknown");
if (i == 3)
xml.append(v->Integer.Value == 0 ? "primary" :
v->Integer.Value == 1 ? "secondary" :
"unknown");
});
}
for (unsigned i = 9; i < obj->Package.Count; i++) {
ACPI_OBJECT * v = &obj->Package.Elements[i];
xml.node(node_name[i], [&] {
if (v->Type != ACPI_TYPE_STRING)
return;
xml.append(v->String.Pointer);
});
}
}
void status(Genode::Xml_generator &xml)
{
/* 10.2.2.6 _BST (Battery Status) */
Acpica::Buffer<char [256]> dynamic;
ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BST"),
nullptr, &dynamic,
ACPI_TYPE_PACKAGE);
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(dynamic.object);
if (ACPI_FAILURE(res) || !obj ||
obj->Package.Count != 4) {
Genode::error("failed - '", __func__, "' _BST res=", Genode::Hex(res));
return;
}
Acpica::Buffer<ACPI_OBJECT> sta;
res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_STA"), nullptr,
&sta, ACPI_TYPE_INTEGER);
if (ACPI_FAILURE(res)) {
xml.node("status", [&] { xml.append("unknown"); });
} else
xml.node("status", [&] {
xml.attribute("value", sta.object.Integer.Value);
/* see "6.3.7 _STA" for more human readable decoding */
if (!(sta.object.Integer.Value & ACPI_STA_BATTERY_PRESENT))
xml.append("battery not present");
});
const char * node_name[] = {
"state", "present_rate", "remaining_capacity",
"present_voltage"
};
if (sizeof(node_name) / sizeof(node_name[0]) != obj->Package.Count)
return;
for (unsigned i = 0; i < obj->Package.Count; i++) {
ACPI_OBJECT * v = &obj->Package.Elements[i];
xml.node(node_name[i], [&] {
if (v->Type != ACPI_TYPE_INTEGER) {
xml.append("unknown");
return;
}
xml.attribute("value", v->Integer.Value);
if (i != 0)
return;
if (v->Integer.Value & 0x1) xml.append("discharging");
if (v->Integer.Value & 0x2) xml.append("charging");
if (v->Integer.Value & 0x4) xml.append("critical low");
});
}
_info(xml);
_status(xml);
}
};