- concurrency and its management
- Race is usually generated as a result of shared access to resources
- When two threads of execution need access to the same data structure (or hardware resources), the likelihood of concurrency always exists
- You should avoid sharing resources whenever possible, but sharing is often necessary, and hardware is essentially shared
- Common techniques for access management are called "locks" or "mutexes"
- Semaphores and mutexes
- Establish critical zones: code can only be executed by one thread at any given moment
- You can use a locking mechanism that allows a process to hibernate when it waits for access to a critical section
- A semaphore is essentially an integer value, which is used in conjunction with a pair of functions, which are often called P and V
- When the semaphore is used for mutual exclusion, the value of the semaphore should be initialized to 1, which is sometimes referred to as a mutex (mutex).
- implementation of Linux semaphores
- <asm/semaphore.h>
- struct semaphore;
- void Sema_init (struct semaphore *sem, int val);
- Declare_mutex (name);
- A semaphore variable called name is initialized to 1
- declare_mutex_locked (name);
- A semaphore variable called name is initialized to 0
- void Init_mutex (struct semaphore *sem);
- void init_mutex_locked (struct semaphore *sem);
- void down (struct semaphore *sem);
- int down_interruptible (struct semaphore *sem);
- operation is interruptible
- If the operation is interrupted, the function returns a non-0 value
- typically uses an interruptible down version
- int Down_trylock (struct semaphore *sem);
- never sleeps
- returns a non-0 value immediately if the semaphore is not available at call
- void up (struct semaphore *sem);
- Reader/writer Semaphore
- Some tasks only need to read the protected data structure, while others must make modifications
- <linux/rwsem.h>
- struct Rw_semaphore;
- void Init_rwsem (struct rw_semaphore *sem);
- void Down_read (struct rw_semaphore *sem);
- Read-only access, which can be accessed concurrently with other readers
- int Down_read_trylock (struct rw_semaphore *sem);
- Returns nonzero when access is granted, and returns zero in other cases
- void Up_read (struct rw_semaphore *sem);
- void Down_write (struct rw_semaphore *sem);
- int Down_write_trylock (struct rw_semaphore *sem);
- void Up_write (struct rw_semaphore *sem);
- void Downgrade_write (struct rw_semaphore *sem);
- It is best to use Rwsem when write access is seldom required and the writer only has short-term semaphores
- Completion
- A common pattern in kernel programming is to initialize an activity outside the current thread, and then wait for the activity to end
- <linux/completion.h>
- Declare_completion (my_completion);
- Init_completion (struct completion *c);
- void Wait_for_completion (struct completion *c);
- void complete (struct completion *c);
- void Complete_all (struct completion *c);
- A completion is usually a one-time (one-shot) device
- If you are not using Complete_all, you can reuse a complete structure
- If Complete_all is used, it must be reinitialized before reusing the structure
- Init_complete (struct completion c);
- void Complete_and_exit (struct completion *c, long retval);
- Spin lock
- Spin locks can be used in code that cannot sleep, such as interrupt processing routines
- delivers higher performance than signal volume
- A spin lock is a mutex, which can only have two values: locking and unlocking
- Typically implemented as a single bit in an integer value
- If the lock is available, the lock bit is set and the code continues to enter the critical section
- If the lock is obtained by someone else, the code goes into a busy loop and checks the lock repeatedly until the lock is available, which is the spin part of the spin lock.
- The "Test and set" operation must be done atomically
- On Hyper-threading processors, which must also be handled carefully to avoid deadlocks, hyper-threading processors can implement multiple virtual CPUs that share a single processor core and cache
- Spin Lock API Introduction
- <linux/spinlock.h>
- spinlock_t
- spinlock_t my_lock = spin_lock_unlocked;
- void Spin_lock_init (spinlock_t *lock);
- void Spin_lock (spinlock_t *lock);
- Non-disruptive
- Spins until the lock is acquired
- void Spin_unlock (spinlock_t *lock);
- Spin Lock and Atomic context
- Any code that has a spin lock must be atomic, cannot hibernate, and cannot abandon the processor for any reason other than service interruption
- Spin lock function
- void Spin_lock (spinlock_t *lock);
- void Spin_lock_irqsave (spinlock_t *lock, unsigned long flags);
- Interrupts are forbidden until the spin lock is obtained, and the previous interrupt state is saved in flags
- void Spin_lock_irq (spinlock_t *lock);
- void Spin_lock_bh (spinlock_t *lock);
- Disable software interruption before acquiring a lock
- void Spin_unlock (spinlock_t *lock);
- void Spin_unlock_irqrestore (spinlock_t *lock, unsigned long flags);
- The flags parameter must be the same variable passed to Spin_lock_irqsave
- void Spin_unlock_irq (spinlock_t *lock);
- void Spin_unlock_bh (spinlock_t *lock);
- int Spin_trylock (spinlock_t *lock);
- Returns a value other than 0 when successful, otherwise returns zero
- int Spin_trylock_bh (spinlock_t *lock);
- Returns a value other than 0 when successful, otherwise returns zero
- Read Value/writer spin lock
- <linux/spinlock.h>
- rwlock_t
- rwlock_t my_rwlock = rw_lock_unlocked;
- void Rwlock_init (rwlock_t * lock);
- void Read_lock (rwlock_t *lock);
- void Read_lock_irqsave (rwlock_t *lock, unsigned long flags);
- void Read_lock_irq (rwlock_t *lock);
- void Read_lock_bh (rwlock_t *lock);
- void Read_unlock (rwlock_t *lock);
- void Read_unlock_irqrestore (rwlock_t *lock, unsigned long flags);
- void Read_unlock_irq (rwlock_t *lock);
- void Read_unlock_bh (rwlock_t *lock);
- void Write_lock (rwlock_t *lock);
- void Write_lock_irqsave (rwlock_t *lock, unsigned long flags);
- void Write_lock_irq (rwlock_t *lock);
- void Write_lock_bh (rwlock_t *lock);
- int Write_trylock (rwlock_t *lock);
- void Write_unlock (rwlock_t *lock);
- void Write_unlock_irqrestore (rwlock_t *lock, unsigned long flags);
- void Write_unlock_irq (rwlock_t *lock);
- void Write_unlock_bh (rwlock_t *lock);
- Reader/writer lock may cause the reader to starve
- Lock traps
- Ambiguous rules
- Neither the semaphore nor the spin lock allows the lock owner to obtain the same lock for the second time, and if it tries to do so, the system hangs
- Order Rules for Locks
- When multiple locks must be acquired, they should always be obtained in the same order
- If you must obtain a local lock and a lock that belongs to a more central location of the kernel, you should first obtain your own local lock
- If you have a combination of semaphores and spin locks, you must first obtain the semaphore
- Contrast between fine-grained locks and coarse-grained locks
- Fine-grained locking with good scalability
- Fine-grained locks will bring some kind of procedural complexity
- Coarse-grained locks should be used initially
- Use the Lockmeter tool to measure how long the kernel spends on locks
- http://oss.sgi.com/projects/lockmeter/
- Methods other than locks
- Lock-Free algorithm
- One of the data structures that is often used for lock-free producer/consumer tasks is the loop buffer
- Atomic variable
- <asm/atomic.h>
- atomic_t
- A atomic_t variable holds an int value, but cannot record an integer greater than 24 bits
- voi D Atomic_set (atomic_t *v, int i);
- atomic_t v = atomic_init (0);
- int Atomic_read (atomic_t *v);
- void Atomic_add (int i, atomic_t *v);
- void atomic_sub (int i, atomic_t *v);
- void Atomic_inc (atomic_t *v);
- void Atomic_dec (atomic_t *v);
- int atomic_inc_and_test (atomic_t *v);
- int atomic_dec_and_test (atomic_t *v);
- int atomic_sub_and_test (int i, atomic_t *v);
- int atomic_add_negative (int i, atomic_t *v);
- int Atomic_add_return (int i, atomic_t *v);
- int Atomic_sub_return (int i, atomic_t *v);
- int Atomic_inc_return (atomic_t *v);
- int Atomic_dec_return (atomic_t *v);
- operations that require multiple atomic_t variables still require some kind of lock
- bit action
- <asm/bitops.h>
- the Nr parameter is typically defined as int, but is defined as unsigned long
- void Set_bit (NR, voi on a few schemas) D *addr);
- void Clear_bit (nr, void *addr);
- void Change_bit (nr, void *addr);
- test_bit (nr, void *addr);
- int test_and_set_bit (nr, void *addr);
- int test_add_clear_bit (nr, void *addr);
- int test_and_change_bit (nr, void *addr);
- Seqlock
- allows the reader free access to the resource, but requires the reader to check for conflicts with the writer
- <linux/seqlock.h>
- seqlock_t
seqlock_t lock1 = seqlock_unlocked;
- void Seqlock_init (seqlock_t *lock);
- unsigned int read_seqbegin (seqlock_t *lock);
- int Read_seqretry (seqlock_t *lock, unsigned int seq);
- unsigned int read_seqbegin_irqsave (seqlock_t *lock, unsigned long flags);
- int Read_seqretry_irqrestore (seqlock_t *lock, unsigned int seq, unsigned long flags);
- void Write_seqlock (seqlock_t *lock);
- void Write_sequnlock (seqlock_t *lock);
- void Write_seqlock_irqsave (seqlock_t *lock, unsigned long flags);
- void Write_seqlock_irq (seqlock_t *lock);
- void Write_seqlock_bh (seqlock_t *lock);
- void Write_sequnlock_irqrestore (seqlock_t *lock, unsigned long flags);
- void Write_sequnlock_irq (seqlock_t *lock);
- void Write_sequnlock_bh (seqlock_t *lock);
- Read-copy-update
- Read-copy-update (RCU) is also an advanced mutex mechanism.
- Rarely used in drivers
- Http://www.rdrop.com/users/paulmck/rclock/intro/rclock_intro.html
- Optimized for situations where reads are frequent and rarely written
- The protected resource should be accessed through a pointer
- When the data structure needs to be modified, the write thread replicates first, then modifies the copy, and then replaces the relevant pointer with the new version. Release the old version when you are sure that the old version has no other references
- <linux/rcupdate.h>
- Rcu_read_lock
- Rcu_read_unlock
- void Call_rcu (struct rcu_head *head, void (*func) (void *arg), void *arg);
"Linux Device Drivers" The fifth chapter concurrency and race--note