| /* Copyright (c) 2009-2011 The Regents of the University of California |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| * |
| * Kernel atomics and locking functions. |
| * |
| * The extern inline declarations are arch-dependent functions. We do this |
| * so that each arch can either static inline or just have a regular function, |
| * whichever is appropriate. The actual implementation usually will be in |
| * arch/atomic.h (for inlines). |
| * |
| * The static inlines are defined farther down in the file (as always). */ |
| |
| #pragma once |
| |
| #include <ros/common.h> |
| #include <ros/atomic.h> |
| #include <arch/membar.h> |
| #include <arch/mmu.h> |
| #include <arch/arch.h> |
| #include <assert.h> |
| |
| /* Atomics */ |
| extern inline void atomic_init(atomic_t *number, long val); |
| extern inline long atomic_read(atomic_t *number); |
| extern inline void atomic_set(atomic_t *number, long val); |
| extern inline void atomic_add(atomic_t *number, long val); |
| extern inline void atomic_inc(atomic_t *number); |
| extern inline void atomic_dec(atomic_t *number); |
| extern inline long atomic_fetch_and_add(atomic_t *number, long val); |
| extern inline void atomic_and(atomic_t *number, long mask); |
| extern inline void atomic_or(atomic_t *number, long mask); |
| extern inline long atomic_swap(atomic_t *addr, long val); |
| extern inline bool atomic_cas(atomic_t *addr, long exp_val, long new_val); |
| extern inline bool atomic_cas_ptr(void **addr, void *exp_val, void *new_val); |
| extern inline bool atomic_cas_u32(uint32_t *addr, uint32_t exp_val, |
| uint32_t new_val); |
| extern inline bool atomic_add_not_zero(atomic_t *number, long val); |
| extern inline bool atomic_sub_and_test(atomic_t *number, long val); |
| |
| /* Spin locks */ |
| struct spinlock { |
| volatile uint32_t rlock; |
| #ifdef CONFIG_SPINLOCK_DEBUG |
| uintptr_t call_site; |
| uint32_t calling_core; |
| bool irq_okay; |
| #endif |
| }; |
| typedef struct spinlock spinlock_t; |
| #define SPINLOCK_INITIALIZER {0} |
| |
| #ifdef CONFIG_SPINLOCK_DEBUG |
| #define SPINLOCK_INITIALIZER_IRQSAVE {0, .irq_okay = TRUE} |
| #else |
| #define SPINLOCK_INITIALIZER_IRQSAVE SPINLOCK_INITIALIZER |
| #endif |
| |
| /* Arch dependent helpers/funcs: */ |
| extern inline void __spinlock_init(spinlock_t *lock); |
| extern inline bool spin_locked(spinlock_t *lock); |
| extern inline void __spin_lock(spinlock_t *lock); |
| extern inline void __spin_unlock(spinlock_t *lock); |
| |
| /* So we can inline a __spin_lock if we want. Even though we don't need this |
| * if we're debugging, its helpful to keep the include at the same place for |
| * all builds. */ |
| #include <arch/atomic.h> |
| |
| #ifdef CONFIG_SPINLOCK_DEBUG |
| /* Arch indep, in k/s/atomic.c */ |
| void spin_lock(spinlock_t *lock); |
| bool spin_trylock(spinlock_t *lock); |
| void spin_unlock(spinlock_t *lock); |
| void spinlock_debug(spinlock_t *lock); |
| |
| #else |
| /* Just inline the arch-specific __ versions */ |
| static inline void spin_lock(spinlock_t *lock) |
| { |
| __spin_lock(lock); |
| } |
| |
| static inline bool spin_trylock(spinlock_t *lock) |
| { |
| return __spin_trylock(lock); |
| } |
| |
| static inline void spin_unlock(spinlock_t *lock) |
| { |
| __spin_unlock(lock); |
| } |
| |
| static inline void spinlock_debug(spinlock_t *lock) |
| { |
| } |
| |
| #endif /* CONFIG_SPINLOCK_DEBUG */ |
| |
| /* Inlines, defined below */ |
| static inline void spinlock_init(spinlock_t *lock); |
| static inline void spinlock_init_irqsave(spinlock_t *lock); |
| static inline void spin_lock_irqsave(spinlock_t *lock); |
| static inline bool spin_trylock_irqsave(spinlock_t *lock); |
| static inline void spin_unlock_irqsave(spinlock_t *lock); |
| static inline bool spin_lock_irq_enabled(spinlock_t *lock); |
| |
| /* Hash locks (array of spinlocks). Most all users will want the default one, |
| * so point your pointer to one of them, though you could always kmalloc a |
| * bigger one. In the future, they might be growable, etc, which init code may |
| * care about. */ |
| struct hashlock { |
| unsigned int nr_entries; |
| struct spinlock locks[]; |
| }; |
| #define HASHLOCK_DEFAULT_SZ 53 /* nice prime, might be a bit large */ |
| struct small_hashlock { |
| unsigned int nr_entries; |
| struct spinlock locks[HASHLOCK_DEFAULT_SZ]; |
| }; |
| |
| void hashlock_init(struct hashlock *hl, unsigned int nr_entries); |
| void hashlock_init_irqsave(struct hashlock *hl, unsigned int nr_entries); |
| void hash_lock(struct hashlock *hl, long key); |
| void hash_unlock(struct hashlock *hl, long key); |
| void hash_lock_irqsave(struct hashlock *hl, long key); |
| void hash_unlock_irqsave(struct hashlock *hl, long key); |
| |
| /* Seq locks */ |
| /* An example seq lock, built from the counter. I don't particularly like this, |
| * since it forces you to use a specific locking type. */ |
| typedef struct seq_lock { |
| spinlock_t w_lock; |
| seq_ctr_t r_ctr; |
| } seqlock_t; |
| |
| static inline void __seq_start_write(seq_ctr_t *seq_ctr); |
| static inline void __seq_end_write(seq_ctr_t *seq_ctr); |
| static inline void write_seqlock(seqlock_t *lock); |
| static inline void write_sequnlock(seqlock_t *lock); |
| static inline seq_ctr_t read_seqbegin(seqlock_t *lock); |
| static inline bool read_seqretry(seqlock_t *lock, seq_ctr_t ctr); |
| |
| /* Post work and poke synchronization. This is a wait-free way to make sure |
| * some code is run, usually by the calling core, but potentially by any core. |
| * Under contention, everyone just posts work, and one core will carry out the |
| * work. Callers post work (the meaning of which is particular to their |
| * subsystem), then call this function. The function is not run concurrently |
| * with itself. |
| * |
| * In the future, this may send RKMs to LL cores to ensure the work is done |
| * somewhere, but not necessarily on the calling core. Will reserve 'flags' |
| * for that. */ |
| struct poke_tracker { |
| atomic_t need_to_run; |
| atomic_t run_in_progress; |
| void (*func)(void *); |
| }; |
| void poke(struct poke_tracker *tracker, void *arg); |
| |
| static inline void poke_init(struct poke_tracker *tracker, void (*func)(void*)) |
| { |
| tracker->need_to_run = 0; |
| tracker->run_in_progress = 0; |
| tracker->func = func; |
| } |
| #define POKE_INITIALIZER(f) {.func = f} |
| |
| /* Arch-specific implementations / declarations go here */ |
| #include <arch/atomic.h> |
| |
| #define MAX_SPINS 1000000000 |
| |
| /* Will spin for a little while, but not deadlock if it never happens */ |
| #define spin_on(x) \ |
| for (int i = 0; (x); i++) { \ |
| cpu_relax(); \ |
| if (i == MAX_SPINS) { \ |
| printk("Probably timed out/failed.\n"); \ |
| break; \ |
| } \ |
| } |
| |
| /*********************** Checklist stuff **********************/ |
| typedef struct checklist_mask { |
| // only need an uint8_t, but we need the bits[] to be word aligned |
| uint32_t size; |
| volatile uint8_t bits[MAX_NUM_CORES]; |
| } checklist_mask_t; |
| |
| // mask contains an unspecified array, so it needs to be at the bottom |
| struct checklist { |
| spinlock_t lock; |
| checklist_mask_t mask; |
| // eagle-eyed readers may know why this might have been needed. |
| // 2009-09-04 |
| //volatile uint8_t (COUNT(BYTES_FOR_BITMASK(size)) bits)[]; |
| }; |
| typedef struct checklist checklist_t; |
| |
| #define ZEROS_ARRAY(size) {[0 ... ((size)-1)] 0} |
| |
| #define DEFAULT_CHECKLIST_MASK(sz) {(sz), ZEROS_ARRAY(BYTES_FOR_BITMASK(sz))} |
| #define DEFAULT_CHECKLIST(sz) {SPINLOCK_INITIALIZER_IRQSAVE, \ |
| DEFAULT_CHECKLIST_MASK(sz)} |
| #define INIT_CHECKLIST(nm, sz) \ |
| checklist_t nm = DEFAULT_CHECKLIST(sz); |
| #define INIT_CHECKLIST_MASK(nm, sz) \ |
| checklist_mask_t nm = DEFAULT_CHECKLIST_MASK(sz); |
| |
| int commit_checklist_wait(checklist_t* list, checklist_mask_t* mask); |
| int commit_checklist_nowait(checklist_t* list, checklist_mask_t* mask); |
| int waiton_checklist(checklist_t* list); |
| int release_checklist(checklist_t* list); |
| int checklist_is_locked(checklist_t* list); |
| int checklist_is_clear(checklist_t* list); |
| int checklist_is_full(checklist_t* list); |
| void reset_checklist(checklist_t* list); |
| void down_checklist(checklist_t* list); |
| // TODO - do we want to adjust the size? (YES, don't want to check it all) |
| // TODO - do we want to be able to call waiton without having called commit? |
| // - in the case of protected checklists |
| // TODO - want a destroy checklist (when we have kmalloc, or whatever) |
| // TODO - some sort of dynamic allocation of them in the future |
| // TODO - think about deadlock issues with one core spinning on a lock for |
| // something that it is the hold out for... |
| // - probably should have interrupts enabled, and never grab these locks |
| // from interrupt context (and not use irq_save) |
| /**************************************************************/ |
| |
| /* Barrier: currently made for everyone barriering. Change to use checklist */ |
| struct barrier { |
| spinlock_t lock; |
| uint32_t init_count; |
| uint32_t current_count; |
| volatile uint8_t ready; |
| }; |
| |
| typedef struct barrier barrier_t; |
| |
| void init_barrier(barrier_t *barrier, uint32_t count); |
| void reset_barrier(barrier_t* barrier); |
| void waiton_barrier(barrier_t* barrier); |
| |
| /* Spinlock bit flags */ |
| #define SPINLOCK_IRQ_EN 0x80000000 |
| |
| static inline void spinlock_init(spinlock_t *lock) |
| { |
| __spinlock_init(lock); |
| #ifdef CONFIG_SPINLOCK_DEBUG |
| lock->call_site = 0; |
| lock->calling_core = 0; |
| lock->irq_okay = FALSE; |
| #endif |
| } |
| |
| static inline void spinlock_init_irqsave(spinlock_t *lock) |
| { |
| __spinlock_init(lock); |
| #ifdef CONFIG_SPINLOCK_DEBUG |
| lock->call_site = 0; |
| lock->calling_core = 0; |
| lock->irq_okay = TRUE; |
| #endif |
| } |
| |
| // If ints are enabled, disable them and note it in the top bit of the lock |
| // There is an assumption about releasing locks in order here... |
| static inline void spin_lock_irqsave(spinlock_t *lock) |
| { |
| uint32_t irq_en; |
| irq_en = irq_is_enabled(); |
| disable_irq(); |
| spin_lock(lock); |
| if (irq_en) |
| lock->rlock |= SPINLOCK_IRQ_EN; |
| } |
| |
| static inline bool spin_trylock_irqsave(spinlock_t *lock) |
| { |
| uint32_t irq_en = irq_is_enabled(); |
| |
| disable_irq(); |
| if (!spin_trylock(lock)) { |
| if (irq_en) |
| enable_irq(); |
| return FALSE; |
| } |
| if (irq_en) |
| lock->rlock |= SPINLOCK_IRQ_EN; |
| return TRUE; |
| } |
| |
| // if the high bit of the lock is set, then re-enable interrupts |
| // (note from asw: you're lucky this works, you little-endian jerks) |
| static inline void spin_unlock_irqsave(spinlock_t *lock) |
| { |
| if (spin_lock_irq_enabled(lock)) { |
| spin_unlock(lock); |
| enable_irq(); |
| } else |
| spin_unlock(lock); |
| } |
| |
| /* Returns whether or not unlocking this lock should enable interrupts or not. |
| * Is meaningless on locks that weren't locked with irqsave. */ |
| static inline bool spin_lock_irq_enabled(spinlock_t *lock) |
| { |
| return lock->rlock & SPINLOCK_IRQ_EN; |
| } |
| |
| /* Note, the seq_ctr is not a full seq lock - just the counter guts. Write |
| * access can be controlled by another lock (like the proc-lock). start_ and |
| * end_write are the writer's responsibility to signal the readers of a |
| * concurrent write. */ |
| static inline void __seq_start_write(seq_ctr_t *seq_ctr) |
| { |
| #ifdef CONFIG_SEQLOCK_DEBUG |
| assert(*seq_ctr % 2 == 0); |
| #endif |
| (*seq_ctr)++; |
| /* We're the only writer, so we need to prevent the compiler (and some |
| * arches) from reordering writes before this point. */ |
| wmb(); |
| } |
| |
| static inline void __seq_end_write(seq_ctr_t *seq_ctr) |
| { |
| #ifdef CONFIG_SEQLOCK_DEBUG |
| assert(*seq_ctr % 2 == 1); |
| #endif |
| /* Need to prevent the compiler (and some arches) from reordering older |
| * stores */ |
| wmb(); |
| (*seq_ctr)++; |
| } |
| |
| /* Untested reference implementation of a seq lock. As mentioned above, we |
| * might need a variety of these (for instance, this doesn't do an irqsave). Or |
| * there may be other invariants that we need the lock to protect. */ |
| static inline void write_seqlock(seqlock_t *lock) |
| { |
| spin_lock(&lock->w_lock); |
| __seq_start_write(&lock->r_ctr); |
| } |
| |
| static inline void write_sequnlock(seqlock_t *lock) |
| { |
| __seq_end_write(&lock->r_ctr); |
| spin_unlock(&lock->w_lock); |
| } |
| |
| static inline seq_ctr_t read_seqbegin(seqlock_t *lock) |
| { |
| seq_ctr_t retval = lock->r_ctr; |
| rmb(); /* don't want future reads to come before our ctr read */ |
| return retval; |
| } |
| |
| static inline bool read_seqretry(seqlock_t *lock, seq_ctr_t ctr) |
| { |
| return seqctr_retry(lock->r_ctr, ctr); |
| } |