{
struct cpu_dbs_info *cdbs = container_of(data, struct cpu_dbs_info, update_util);
struct policy_dbs_info *policy_dbs = cdbs->policy_dbs;
- u64 delta_ns;
+ u64 delta_ns, lst;
/*
* The work may not be allowed to be queued up right now.
* of sample_delay_ns used in the computation may be stale.
*/
smp_rmb();
- delta_ns = time - policy_dbs->last_sample_time;
+ lst = READ_ONCE(policy_dbs->last_sample_time);
+ delta_ns = time - lst;
if ((s64)delta_ns < policy_dbs->sample_delay_ns)
return;
* at this point. Otherwise, we need to ensure that only one of the
* CPUs sharing the policy will do that.
*/
- if (policy_dbs->is_shared &&
- !atomic_add_unless(&policy_dbs->work_count, 1, 1))
- return;
+ if (policy_dbs->is_shared) {
+ if (!atomic_add_unless(&policy_dbs->work_count, 1, 1))
+ return;
+
+ /*
+ * If another CPU updated last_sample_time in the meantime, we
+ * shouldn't be here, so clear the work counter and bail out.
+ */
+ if (unlikely(lst != READ_ONCE(policy_dbs->last_sample_time))) {
+ atomic_set(&policy_dbs->work_count, 0);
+ return;
+ }
+ }
policy_dbs->last_sample_time = time;
policy_dbs->work_in_progress = true;