base: fix quota reversing when clients vanish

The 'Child::_revert_quota_and_destroy' assumes to be called from the
client's context, which is normally the case when destroying sessions.
However, if a client's session outlives the client (because the
asynchronous close request to the server is still pending), the session
cleanup is performed in the context of the server. Here, the
'session_response' implementation wrongly called
'_revert_quota_and_destroy' to the effect that the session quota was
withdrawn from the server (good) but subsequently transferred back to
the server (bad). The patch replaces the call of
'_revert_quota_and_destroy' with only the first - correct - part of the
transaction.
This commit is contained in:
Norman Feske 2018-06-07 12:02:38 +02:00 committed by Christian Helmuth
parent 7088e4faaa
commit 578bec11ac

View File

@ -544,10 +544,45 @@ void Child::session_response(Server::Id id, Session_response response)
* i.e., if the close request was issued by ourself while
* killing a child, we drop the session state immediately.
*/
if (session.closed_callback)
if (session.closed_callback) {
session.closed_callback->session_closed(session);
else
_revert_quota_and_destroy(session);
} else {
/*
* The client no longer exists. So we cannot take the
* regular path of executing '_revert_quota_and_destroy' in
* the context of the client. Instead, we immediately
* withdraw the session quota from the server ('this') to
* the reference account, and destroy the session object.
*/
Ram_transfer::Remote_account ref_ram_account(_policy.ref_pd(), _policy.ref_pd_cap());
Ram_transfer::Account &service_ram_account = session.service();
Cap_transfer::Remote_account ref_cap_account(_policy.ref_pd(), _policy.ref_pd_cap());
Cap_transfer::Account &service_cap_account = session.service();
try {
/* transfer session quota from the service to ourself */
Ram_transfer ram_donation_from_service(session.donated_ram_quota(),
service_ram_account, ref_ram_account);
Cap_transfer cap_donation_from_service(session.donated_cap_quota(),
service_cap_account, ref_cap_account);
ram_donation_from_service.acknowledge();
cap_donation_from_service.acknowledge();
}
catch (Ram_transfer::Quota_exceeded) {
warning(_policy.name(), " failed to return session RAM quota "
"(", session.donated_ram_quota(), ")"); }
catch (Cap_transfer::Quota_exceeded) {
warning(_policy.name(), " failed to return session cap quota "
"(", session.donated_cap_quota(), ")"); }
session.destroy();
_policy.session_state_changed();
}
break;
case Parent::SERVICE_DENIED: