/* * We have two copies, and flip between them to make it seem like an atomic * update. The update is not really atomic wrt the events counter, but * it is internally consistent with the bit layout depending on shift. * * We copy the events count, move the bits around and flip the index. */ void prop_change_shift(struct prop_descriptor *pd, int shift) { int index; int offset; u64 events; unsigned long flags; if (shift > PROP_MAX_SHIFT) shift = PROP_MAX_SHIFT; mutex_lock(&pd->mutex); index = pd->index ^ 1; offset = pd->pg[pd->index].shift - shift; if (!offset) goto out; pd->pg[index].shift = shift; local_irq_save(flags); events = percpu_counter_sum(&pd->pg[pd->index].events); if (offset < 0) events <<= -offset; else events >>= offset; percpu_counter_set(&pd->pg[index].events, events); /* * ensure the new pg is fully written before the switch */ smp_wmb(); pd->index = index; local_irq_restore(flags); synchronize_rcu(); out: mutex_unlock(&pd->mutex); }
/* * Compare counter against given value. * Return 1 if greater, 0 if equal and -1 if less */ int __percpu_counter_compare(struct percpu_counter *fbc, int64_t rhs, int32_t batch) { int64_t count; count = percpu_counter_read(fbc); /* Check to see if rough count will be sufficient for comparison */ if (abs(count - rhs) > (batch * num_online_cpus())) { if (count > rhs) return 1; else return -1; } /* Need to use precise count */ count = percpu_counter_sum(fbc); if (count > rhs) return 1; else if (count < rhs) return -1; else return 0; }
/* * Catch up with missed period expirations. * * until (c_{j} == c) * x_{j} -= x_{j}/2; * c_{j}++; */ static void prop_norm_percpu(struct prop_global *pg, struct prop_local_percpu *pl) { unsigned long period = 1UL << (pg->shift - 1); unsigned long period_mask = ~(period - 1); unsigned long global_period; unsigned long flags; global_period = percpu_counter_read(&pg->events); global_period &= period_mask; /* * Fast path - check if the local and global period count still match * outside of the lock. */ if (pl->period == global_period) return; raw_spin_lock_irqsave(&pl->lock, flags); prop_adjust_shift(&pl->shift, &pl->period, pg->shift); /* * For each missed period, we half the local counter. * basically: * pl->events >> (global_period - pl->period); */ period = (global_period - pl->period) >> (pg->shift - 1); if (period < BITS_PER_LONG) { s64 val = percpu_counter_read(&pl->events); if (val < (nr_cpu_ids * PROP_BATCH)) val = percpu_counter_sum(&pl->events); __percpu_counter_add(&pl->events, -val + (val >> period), PROP_BATCH); } else
static void prop_norm_percpu(struct prop_global *pg, struct prop_local_percpu *pl) { unsigned long period = 1UL << (pg->shift - 1); unsigned long period_mask = ~(period - 1); unsigned long global_period; unsigned long flags; global_period = percpu_counter_read(&pg->events); global_period &= period_mask; /* */ if (pl->period == global_period) return; raw_spin_lock_irqsave(&pl->lock, flags); prop_adjust_shift(&pl->shift, &pl->period, pg->shift); /* */ period = (global_period - pl->period) >> (pg->shift - 1); if (period < BITS_PER_LONG) { s64 val = percpu_counter_read(&pl->events); if (val < (nr_cpu_ids * PROP_BATCH)) val = percpu_counter_sum(&pl->events); __percpu_counter_add(&pl->events, -val + (val >> period), PROP_BATCH); } else
uint64_t read_partitioned_counter(PARTITIONED_COUNTER pc) { return percpu_counter_sum(&pc->pcpu_counter); }
/* * blocked until all in-flight bios operations are finished. */ static void btrfs_rm_dev_replace_blocked(struct btrfs_fs_info *fs_info) { set_bit(BTRFS_FS_STATE_DEV_REPLACING, &fs_info->fs_state); wait_event(fs_info->dev_replace.replace_wait, !percpu_counter_sum( &fs_info->dev_replace.bio_counter)); }