[broker] Prevent contingent lease from satisfying passive claim
* Prevent active claims from a still-contingent lease from satisfying a
passive claim.
* When a lease transitions from satisfied to pending and becomes
contingent, scan for the elements of any active claims it holds and
check for any passive claims of those elements. Re-evaluate their
leases, as they may no longer be satisfied.
Bug: 335451077
Test: fx test src/power/broker
Change-Id: I50dd76d0f35611b9e12ba014f67c78094b0a6367
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1037944
Fuchsia-Auto-Submit: Prashanth Swaminathan <prashanthsw@google.com>
Reviewed-by: Kyle Gong <kgong@google.com>
Reviewed-by: Justin Mattson <jmatt@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
diff --git a/src/power/broker/src/broker.rs b/src/power/broker/src/broker.rs
index ae4c643..050b20e 100644
--- a/src/power/broker/src/broker.rs
+++ b/src/power/broker/src/broker.rs
@@ -317,9 +317,9 @@
}
/// Returns true if the lease has one or more passive claims with no active
- /// claims belonging to other leases that would satisfy them.
- /// (If a lease is contingent on any of its passive claims, none of its
- /// claims should be activated.)
+ /// claims belonging to other leases that would satisfy them. Such active claims
+ /// must not themselves be contingent. If a lease is contingent on any of its
+ /// passive claims, none of its claims should be activated.
fn is_lease_contingent(&self, lease_id: &LeaseID) -> bool {
let passive_claims = self
.catalog
@@ -344,6 +344,7 @@
let matching_active_claim_found = activated_claims
.into_iter()
.chain(pending_claims)
+ .filter(|c| !self.is_lease_contingent(&c.lease_id))
.any(|c| c.requires().level.satisfies(claim.requires().level));
if !matching_active_claim_found {
return true;
@@ -407,6 +408,30 @@
tracing::warn!("update_lease_status: lease {lease_id} not found");
}
tracing::debug!("update_lease_status({lease_id}) to {status:?}");
+ // If this lease has transitioned into pending and is now contingent,
+ // scan active claims of this lease. If those active claims have any
+ // passive claims sharing the required element-level of D's active claims,
+ // then re-evaluate their lease to determine if it's newly pending.
+ if prev_status.as_ref() == Some(&LeaseStatus::Satisfied)
+ && status == LeaseStatus::Pending
+ && self.is_lease_contingent(lease_id)
+ {
+ for active_claim in self.catalog.active_claims.activated.for_lease(lease_id) {
+ let element_of_pending_lease = &active_claim.requires().element_id;
+ for passive_claim in self
+ .catalog
+ .passive_claims
+ .activated
+ .for_required_element(element_of_pending_lease)
+ {
+ if &passive_claim.lease_id != lease_id
+ && active_claim.requires().level.satisfies(passive_claim.requires().level)
+ {
+ self.update_lease_status(&passive_claim.lease_id);
+ }
+ }
+ }
+ }
Some(status)
}
@@ -4174,6 +4199,407 @@
}
#[fuchsia::test]
+ async fn test_active_claims_do_not_satisfy_passive_claims_while_contingent() {
+ // Test that active claims from a contingent lease do not satisfy passive claims until that
+ // lease is made non-contingent.
+ //
+ // B has an active dependency on C.
+ // D has an active dependency on A and a passive dependency on C.
+ // E has a passive dependency on A.
+ // A B C D E
+ // ON => ON
+ // ON <============= ON
+ // ON <- ON
+ // ON <------------------- ON
+ let mut broker = Broker::new();
+ let token_a_active = DependencyToken::create();
+ let token_a_passive = DependencyToken::create();
+ let element_a = broker
+ .add_element(
+ "A",
+ BinaryPowerLevel::Off.into_primitive(),
+ BINARY_POWER_LEVELS.to_vec(),
+ vec![],
+ vec![token_a_active
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into()],
+ vec![token_a_passive
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into()],
+ )
+ .expect("add_element failed");
+ let token_c_active = DependencyToken::create();
+ let token_c_passive = DependencyToken::create();
+ let element_c = broker
+ .add_element(
+ "C",
+ BinaryPowerLevel::Off.into_primitive(),
+ BINARY_POWER_LEVELS.to_vec(),
+ vec![],
+ vec![token_c_active
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into()],
+ vec![token_c_passive
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into()],
+ )
+ .expect("add_element failed");
+ let token_b_active = DependencyToken::create();
+ let element_b = broker
+ .add_element(
+ "B",
+ BinaryPowerLevel::Off.into_primitive(),
+ BINARY_POWER_LEVELS.to_vec(),
+ vec![fpb::LevelDependency {
+ dependency_type: DependencyType::Active,
+ dependent_level: BinaryPowerLevel::On.into_primitive(),
+ requires_token: token_c_active
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into(),
+ requires_level: BinaryPowerLevel::On.into_primitive(),
+ }],
+ vec![token_b_active
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into()],
+ vec![],
+ )
+ .expect("add_element failed");
+ let element_d = broker
+ .add_element(
+ "D",
+ BinaryPowerLevel::Off.into_primitive(),
+ BINARY_POWER_LEVELS.to_vec(),
+ vec![
+ fpb::LevelDependency {
+ dependency_type: DependencyType::Active,
+ dependent_level: BinaryPowerLevel::On.into_primitive(),
+ requires_token: token_a_active
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into(),
+ requires_level: BinaryPowerLevel::On.into_primitive(),
+ },
+ fpb::LevelDependency {
+ dependency_type: DependencyType::Passive,
+ dependent_level: BinaryPowerLevel::On.into_primitive(),
+ requires_token: token_c_passive
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into(),
+ requires_level: BinaryPowerLevel::On.into_primitive(),
+ },
+ ],
+ vec![],
+ vec![],
+ )
+ .expect("add_element failed");
+ let element_e = broker
+ .add_element(
+ "E",
+ BinaryPowerLevel::Off.into_primitive(),
+ BINARY_POWER_LEVELS.to_vec(),
+ vec![fpb::LevelDependency {
+ dependency_type: DependencyType::Passive,
+ dependent_level: BinaryPowerLevel::On.into_primitive(),
+ requires_token: token_a_passive
+ .duplicate_handle(zx::Rights::SAME_RIGHTS)
+ .expect("dup failed")
+ .into(),
+ requires_level: BinaryPowerLevel::On.into_primitive(),
+ }],
+ vec![],
+ vec![],
+ )
+ .expect("add_element failed");
+
+ // Initial required level for A and C should be OFF.
+ // Set A and C's current level to OFF.
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ broker.update_current_level(&element_a, BinaryPowerLevel::Off.into_primitive());
+ broker.update_current_level(&element_c, BinaryPowerLevel::Off.into_primitive());
+
+ // Lease E.
+ // A's required level should remain OFF because of E's passive claim.
+ // B, C, D & E's required level are unaffected and should remain OFF.
+ // Lease E should be pending and contingent on its passive claim.
+ let lease_e = broker
+ .acquire_lease(&element_e, BinaryPowerLevel::On.into_primitive())
+ .expect("acquire failed");
+ let lease_e_id = lease_e.id.clone();
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(broker.get_lease_status(&lease_e.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.is_lease_contingent(&lease_e.id), true);
+
+ // Lease D.
+ // A's required level should remain OFF. While Lease D has an active claim on A, it is not
+ // activated until it is not contingent on C.
+ // C's required level should remain OFF because of D's passive claim.
+ // B, D & E's required level are unaffected and should remain OFF.
+ // Lease D should be pending and contingent on its passive claim.
+ // Lease E should be pending and contingent on its passive claim, which is not satisfied
+ // by Lease D's active claim, as Lease D is contingent.
+ let lease_d = broker
+ .acquire_lease(&element_d, BinaryPowerLevel::On.into_primitive())
+ .expect("acquire failed");
+ let lease_d_id = lease_d.id.clone();
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(broker.get_lease_status(&lease_d.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.get_lease_status(&lease_e.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.is_lease_contingent(&lease_d.id), true);
+ assert_eq!(broker.is_lease_contingent(&lease_e.id), true);
+
+ // Lease B.
+ // A should have required level ON, because D is no longer contingent on C.
+ // C should have required level ON, because of B's active claim.
+ // B, D & E's required level are unaffected and should remain OFF.
+ // Lease B should be pending and not contingent.
+ // Lease D should be pending and not contingent.
+ // Lease E should be pending and not contingent.
+ let lease_b = broker
+ .acquire_lease(&element_b, BinaryPowerLevel::On.into_primitive())
+ .expect("acquire failed");
+ let lease_b_id = lease_b.id.clone();
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(broker.get_lease_status(&lease_b.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.get_lease_status(&lease_d.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.get_lease_status(&lease_e.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.is_lease_contingent(&lease_b.id), false);
+ assert_eq!(broker.is_lease_contingent(&lease_d.id), false);
+ assert_eq!(broker.is_lease_contingent(&lease_e.id), false);
+
+ // Update C's current level to ON.
+ // A should still have required level ON.
+ // B should have required level ON, now that it's lease is satisfied.
+ // C should still have required level ON.
+ // D & E's required level are unaffected and should remain OFF.
+ // Lease B should now be satisfied and not contingent.
+ // Lease D and E should be pending as A is not yet ON, and not contingent.
+ broker.update_current_level(&element_c, BinaryPowerLevel::On.into_primitive());
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(broker.get_lease_status(&lease_b.id), Some(LeaseStatus::Satisfied));
+ assert_eq!(broker.get_lease_status(&lease_d.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.get_lease_status(&lease_e.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.is_lease_contingent(&lease_b.id), false);
+ assert_eq!(broker.is_lease_contingent(&lease_d.id), false);
+ assert_eq!(broker.is_lease_contingent(&lease_e.id), false);
+
+ // Update A's current level to ON.
+ // A should still have required level ON.
+ // B should still have required level ON.
+ // C should still have required level ON.
+ // D & E's required level should be ON, now that their leases are satisfied.
+ // Lease B, D and E should now all be satisfied.
+ broker.update_current_level(&element_a, BinaryPowerLevel::On.into_primitive());
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(broker.get_lease_status(&lease_b.id), Some(LeaseStatus::Satisfied));
+ assert_eq!(broker.get_lease_status(&lease_d.id), Some(LeaseStatus::Satisfied));
+ assert_eq!(broker.get_lease_status(&lease_e.id), Some(LeaseStatus::Satisfied));
+ assert_eq!(broker.is_lease_contingent(&lease_b.id), false);
+ assert_eq!(broker.is_lease_contingent(&lease_d.id), false);
+ assert_eq!(broker.is_lease_contingent(&lease_e.id), false);
+
+ // Drop lease for B.
+ // A should still have required level ON.
+ // C should still have required level ON.
+ // B, D & E's required levels are now OFF as their leases are no longer satisfied.
+ // Lease D should drop to pending and contingent.
+ // Lease E should drop to pending and contingent.
+ broker.drop_lease(&lease_b.id).expect("drop_lease failed");
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(broker.get_lease_status(&lease_d.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.get_lease_status(&lease_e.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.is_lease_contingent(&lease_d.id), true);
+ assert_eq!(broker.is_lease_contingent(&lease_e.id), true);
+
+ // Drop lease for D.
+ // A should still have required level ON.
+ // C should have required level OFF (no leases require C anymore).
+ // B, D & E's required level are unaffected and should remain OFF.
+ // Lease E should remain at pending and contingent.
+ broker.drop_lease(&lease_d.id).expect("drop_lease failed");
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::On.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(broker.get_lease_status(&lease_e.id), Some(LeaseStatus::Pending));
+ assert_eq!(broker.is_lease_contingent(&lease_e.id), true);
+
+ // Drop lease for E.
+ // A should have required level OFF.
+ // C should still have required level OFF.
+ // B, D & E's required level are unaffected and should remain OFF.
+ broker.drop_lease(&lease_e.id).expect("drop_lease failed");
+ assert_eq!(
+ broker.get_required_level(&element_a),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_b),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_c),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_d),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+ assert_eq!(
+ broker.get_required_level(&element_e),
+ Some(BinaryPowerLevel::Off.into_primitive())
+ );
+
+ // Leases B, D and E should be cleaned up.
+ assert_lease_cleaned_up(&broker.catalog, &lease_b_id);
+ assert_lease_cleaned_up(&broker.catalog, &lease_d_id);
+ assert_lease_cleaned_up(&broker.catalog, &lease_e_id);
+ }
+
+ #[fuchsia::test]
async fn test_lease_passive_chained() {
// Tests that active dependencies, which depend on passive dependencies,
// which in turn depend on active dependencies, all work as expected.