diff options
| author | Linus Torvalds <[email protected]> | 2009-06-16 11:30:37 -0700 | 
|---|---|---|
| committer | Linus Torvalds <[email protected]> | 2009-06-16 11:30:37 -0700 | 
| commit | 609106b9ac968adbc76ce78c979fc3903a56e16c (patch) | |
| tree | 4af8b305ab4095870a927ffdb9a5e14eb2107401 /lib/atomic64.c | |
| parent | 69257cae20640a396f03aa0bf169b815ba66a58a (diff) | |
| parent | 42e27bfc4bfa42bd905e53be93d862b8e3d80a00 (diff) | |
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (38 commits)
  ps3flash: Always read chunks of 256 KiB, and cache them
  ps3flash: Cache the last accessed FLASH chunk
  ps3: Replace direct file operations by callback
  ps3: Switch ps3_os_area_[gs]et_rtc_diff to EXPORT_SYMBOL_GPL()
  ps3: Correct debug message in dma_ioc0_map_pages()
  drivers/ps3: Add missing annotations
  ps3fb: Use ps3_system_bus_[gs]et_drvdata() instead of direct access
  ps3flash: Use ps3_system_bus_[gs]et_drvdata() instead of direct access
  ps3: shorten ps3_system_bus_[gs]et_driver_data to ps3_system_bus_[gs]et_drvdata
  ps3: Use dev_[gs]et_drvdata() instead of direct access for system bus devices
  block/ps3: remove driver_data direct access of struct device
  ps3vram: Make ps3vram_priv.reports a void *
  ps3vram: Remove no longer used ps3vram_priv.ddr_base
  ps3vram: Replace mutex by spinlock + bio_list
  block: Add bio_list_peek()
  powerpc: Use generic atomic64_t implementation on 32-bit processors
  lib: Provide generic atomic64_t implementation
  powerpc: Add compiler memory barrier to mtmsr macro
  powerpc/iseries: Mark signal_vsp_instruction() as maybe unused
  powerpc/iseries: Fix unused function warning in iSeries DT code
  ...
Diffstat (limited to 'lib/atomic64.c')
| -rw-r--r-- | lib/atomic64.c | 175 | 
1 files changed, 175 insertions, 0 deletions
diff --git a/lib/atomic64.c b/lib/atomic64.c new file mode 100644 index 000000000000..c5e725562416 --- /dev/null +++ b/lib/atomic64.c @@ -0,0 +1,175 @@ +/* + * Generic implementation of 64-bit atomics using spinlocks, + * useful on processors that don't have 64-bit atomic instructions. + * + * Copyright © 2009 Paul Mackerras, IBM Corp. <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/cache.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <asm/atomic.h> + +/* + * We use a hashed array of spinlocks to provide exclusive access + * to each atomic64_t variable.  Since this is expected to used on + * systems with small numbers of CPUs (<= 4 or so), we use a + * relatively small array of 16 spinlocks to avoid wasting too much + * memory on the spinlock array. + */ +#define NR_LOCKS	16 + +/* + * Ensure each lock is in a separate cacheline. + */ +static union { +	spinlock_t lock; +	char pad[L1_CACHE_BYTES]; +} atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp; + +static inline spinlock_t *lock_addr(const atomic64_t *v) +{ +	unsigned long addr = (unsigned long) v; + +	addr >>= L1_CACHE_SHIFT; +	addr ^= (addr >> 8) ^ (addr >> 16); +	return &atomic64_lock[addr & (NR_LOCKS - 1)].lock; +} + +long long atomic64_read(const atomic64_t *v) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); +	long long val; + +	spin_lock_irqsave(lock, flags); +	val = v->counter; +	spin_unlock_irqrestore(lock, flags); +	return val; +} + +void atomic64_set(atomic64_t *v, long long i) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); + +	spin_lock_irqsave(lock, flags); +	v->counter = i; +	spin_unlock_irqrestore(lock, flags); +} + +void atomic64_add(long long a, atomic64_t *v) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); + +	spin_lock_irqsave(lock, flags); +	v->counter += a; +	spin_unlock_irqrestore(lock, flags); +} + +long long atomic64_add_return(long long a, atomic64_t *v) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); +	long long val; + +	spin_lock_irqsave(lock, flags); +	val = v->counter += a; +	spin_unlock_irqrestore(lock, flags); +	return val; +} + +void atomic64_sub(long long a, atomic64_t *v) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); + +	spin_lock_irqsave(lock, flags); +	v->counter -= a; +	spin_unlock_irqrestore(lock, flags); +} + +long long atomic64_sub_return(long long a, atomic64_t *v) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); +	long long val; + +	spin_lock_irqsave(lock, flags); +	val = v->counter -= a; +	spin_unlock_irqrestore(lock, flags); +	return val; +} + +long long atomic64_dec_if_positive(atomic64_t *v) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); +	long long val; + +	spin_lock_irqsave(lock, flags); +	val = v->counter - 1; +	if (val >= 0) +		v->counter = val; +	spin_unlock_irqrestore(lock, flags); +	return val; +} + +long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); +	long long val; + +	spin_lock_irqsave(lock, flags); +	val = v->counter; +	if (val == o) +		v->counter = n; +	spin_unlock_irqrestore(lock, flags); +	return val; +} + +long long atomic64_xchg(atomic64_t *v, long long new) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); +	long long val; + +	spin_lock_irqsave(lock, flags); +	val = v->counter; +	v->counter = new; +	spin_unlock_irqrestore(lock, flags); +	return val; +} + +int atomic64_add_unless(atomic64_t *v, long long a, long long u) +{ +	unsigned long flags; +	spinlock_t *lock = lock_addr(v); +	int ret = 1; + +	spin_lock_irqsave(lock, flags); +	if (v->counter != u) { +		v->counter += a; +		ret = 0; +	} +	spin_unlock_irqrestore(lock, flags); +	return ret; +} + +static int init_atomic64_lock(void) +{ +	int i; + +	for (i = 0; i < NR_LOCKS; ++i) +		spin_lock_init(&atomic64_lock[i].lock); +	return 0; +} + +pure_initcall(init_atomic64_lock);  |