2017-03-16 22:18:50 -08:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/export.h>
# include <linux/log2.h>
# include <linux/percpu.h>
# include <linux/preempt.h>
# include <linux/rcupdate.h>
# include <linux/sched.h>
2022-09-24 01:33:13 -04:00
# include <linux/sched/clock.h>
2017-03-16 22:18:50 -08:00
# include <linux/sched/rt.h>
2023-08-12 17:10:42 -04:00
# include <linux/sched/task.h>
2017-03-16 22:18:50 -08:00
# include <linux/slab.h>
# include "six.h"
# ifdef DEBUG
2023-05-20 23:57:48 -04:00
# define EBUG_ON(cond) BUG_ON(cond)
2017-03-16 22:18:50 -08:00
# else
2023-05-20 23:57:48 -04:00
# define EBUG_ON(cond) do {} while (0)
2017-03-16 22:18:50 -08:00
# endif
2023-02-04 19:38:43 -05:00
# define six_acquire(l, t, r, ip) lock_acquire(l, 0, t, r, 1, NULL, ip)
# define six_release(l, ip) lock_release(l, ip)
2017-03-16 22:18:50 -08:00
2022-08-26 19:22:24 -04:00
static void do_six_unlock_type ( struct six_lock * lock , enum six_lock_type type ) ;
2023-05-22 12:11:13 -04:00
# define SIX_LOCK_HELD_read_OFFSET 0
# define SIX_LOCK_HELD_read ~(~0U << 26)
# define SIX_LOCK_HELD_intent (1U << 26)
# define SIX_LOCK_HELD_write (1U << 27)
# define SIX_LOCK_WAITING_read (1U << (28 + SIX_LOCK_read))
# define SIX_LOCK_WAITING_write (1U << (28 + SIX_LOCK_write))
# define SIX_LOCK_NOSPIN (1U << 31)
2023-05-20 23:57:48 -04:00
2017-03-16 22:18:50 -08:00
struct six_lock_vals {
/* Value we add to the lock in order to take the lock: */
2023-05-22 00:17:40 -04:00
u32 lock_val ;
2017-03-16 22:18:50 -08:00
/* If the lock has this value (used as a mask), taking the lock fails: */
2023-05-22 00:17:40 -04:00
u32 lock_fail ;
2017-03-16 22:18:50 -08:00
/* Mask that indicates lock is held for this type: */
2023-05-22 00:17:40 -04:00
u32 held_mask ;
2017-03-16 22:18:50 -08:00
/* Waitlist we wakeup when releasing the lock: */
enum six_lock_type unlock_wakeup ;
} ;
2023-06-16 19:21:21 -04:00
static const struct six_lock_vals l [ ] = {
[ SIX_LOCK_read ] = {
2023-05-22 12:11:13 -04:00
. lock_val = 1U < < SIX_LOCK_HELD_read_OFFSET ,
2023-06-16 19:21:21 -04:00
. lock_fail = SIX_LOCK_HELD_write ,
. held_mask = SIX_LOCK_HELD_read ,
. unlock_wakeup = SIX_LOCK_write ,
} ,
[ SIX_LOCK_intent ] = {
2023-05-22 12:11:13 -04:00
. lock_val = SIX_LOCK_HELD_intent ,
2023-06-16 19:21:21 -04:00
. lock_fail = SIX_LOCK_HELD_intent ,
. held_mask = SIX_LOCK_HELD_intent ,
. unlock_wakeup = SIX_LOCK_intent ,
} ,
[ SIX_LOCK_write ] = {
. lock_val = SIX_LOCK_HELD_write ,
. lock_fail = SIX_LOCK_HELD_read ,
. held_mask = SIX_LOCK_HELD_write ,
. unlock_wakeup = SIX_LOCK_read ,
} ,
} ;
2017-03-16 22:18:50 -08:00
2023-05-22 00:17:40 -04:00
static inline void six_set_bitmask ( struct six_lock * lock , u32 mask )
2023-05-20 23:57:48 -04:00
{
2023-05-22 00:17:40 -04:00
if ( ( atomic_read ( & lock - > state ) & mask ) ! = mask )
atomic_or ( mask , & lock - > state ) ;
2023-05-20 23:57:48 -04:00
}
2023-05-22 00:17:40 -04:00
static inline void six_clear_bitmask ( struct six_lock * lock , u32 mask )
2023-05-20 23:57:48 -04:00
{
2023-05-22 00:17:40 -04:00
if ( atomic_read ( & lock - > state ) & mask )
atomic_and ( ~ mask , & lock - > state ) ;
2023-05-20 23:57:48 -04:00
}
2017-03-16 22:18:50 -08:00
static inline void six_set_owner ( struct six_lock * lock , enum six_lock_type type ,
2023-05-22 00:17:40 -04:00
u32 old , struct task_struct * owner )
2017-03-16 22:18:50 -08:00
{
if ( type ! = SIX_LOCK_intent )
return ;
2023-05-20 23:57:48 -04:00
if ( ! ( old & SIX_LOCK_HELD_intent ) ) {
2017-03-16 22:18:50 -08:00
EBUG_ON ( lock - > owner ) ;
2022-08-26 19:22:24 -04:00
lock - > owner = owner ;
2017-03-16 22:18:50 -08:00
} else {
EBUG_ON ( lock - > owner ! = current ) ;
}
}
static inline unsigned pcpu_read_count ( struct six_lock * lock )
{
unsigned read_count = 0 ;
int cpu ;
for_each_possible_cpu ( cpu )
read_count + = * per_cpu_ptr ( lock - > readers , cpu ) ;
return read_count ;
}
2023-05-21 23:41:56 -04:00
/*
* __do_six_trylock ( ) - main trylock routine
*
* Returns 1 on success , 0 on failure
*
* In percpu reader mode , a failed trylock may cause a spurious trylock failure
* for anoter thread taking the competing lock type , and we may havve to do a
* wakeup : when a wakeup is required , we return - 1 - wakeup_type .
*/
2023-05-21 15:40:40 -04:00
static int __do_six_trylock ( struct six_lock * lock , enum six_lock_type type ,
struct task_struct * task , bool try )
2017-03-16 22:18:50 -08:00
{
2022-08-26 19:22:24 -04:00
int ret ;
2023-05-25 14:35:06 -04:00
u32 old ;
2017-03-16 22:18:50 -08:00
2022-08-26 19:22:24 -04:00
EBUG_ON ( type = = SIX_LOCK_write & & lock - > owner ! = task ) ;
2023-05-20 23:57:48 -04:00
EBUG_ON ( type = = SIX_LOCK_write & &
2023-05-22 12:11:13 -04:00
( try ! = ! ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_write ) ) ) ;
2017-03-16 22:18:50 -08:00
/*
* Percpu reader mode :
*
* The basic idea behind this algorithm is that you can implement a lock
* between two threads without any atomics , just memory barriers :
*
* For two threads you ' ll need two variables , one variable for " thread a
* has the lock " and another for " thread b has the lock " .
*
* To take the lock , a thread sets its variable indicating that it holds
* the lock , then issues a full memory barrier , then reads from the
* other thread ' s variable to check if the other thread thinks it has
* the lock . If we raced , we backoff and retry / sleep .
2023-05-21 23:41:56 -04:00
*
* Failure to take the lock may cause a spurious trylock failure in
* another thread , because we temporarily set the lock to indicate that
* we held it . This would be a problem for a thread in six_lock ( ) , when
* they are calling trylock after adding themself to the waitlist and
* prior to sleeping .
*
* Therefore , if we fail to get the lock , and there were waiters of the
* type we conflict with , we will have to issue a wakeup .
*
* Since we may be called under wait_lock ( and by the wakeup code
* itself ) , we return that the wakeup has to be done instead of doing it
* here .
2017-03-16 22:18:50 -08:00
*/
if ( type = = SIX_LOCK_read & & lock - > readers ) {
preempt_disable ( ) ;
this_cpu_inc ( * lock - > readers ) ; /* signal that we own lock */
smp_mb ( ) ;
2023-05-22 00:17:40 -04:00
old = atomic_read ( & lock - > state ) ;
2023-05-20 23:57:48 -04:00
ret = ! ( old & l [ type ] . lock_fail ) ;
2017-03-16 22:18:50 -08:00
this_cpu_sub ( * lock - > readers , ! ret ) ;
preempt_enable ( ) ;
2023-05-22 12:11:13 -04:00
if ( ! ret & & ( old & SIX_LOCK_WAITING_write ) )
2022-08-26 19:22:24 -04:00
ret = - 1 - SIX_LOCK_write ;
2017-03-16 22:18:50 -08:00
} else if ( type = = SIX_LOCK_write & & lock - > readers ) {
if ( try ) {
2023-05-22 12:11:13 -04:00
atomic_add ( SIX_LOCK_HELD_write , & lock - > state ) ;
2017-03-16 22:18:50 -08:00
smp_mb__after_atomic ( ) ;
}
ret = ! pcpu_read_count ( lock ) ;
2023-06-16 18:24:05 -04:00
if ( try & & ! ret ) {
2023-05-22 12:11:13 -04:00
old = atomic_sub_return ( SIX_LOCK_HELD_write , & lock - > state ) ;
if ( old & SIX_LOCK_WAITING_read )
2022-08-26 19:22:24 -04:00
ret = - 1 - SIX_LOCK_read ;
2017-03-16 22:18:50 -08:00
}
} else {
2023-05-25 14:35:06 -04:00
old = atomic_read ( & lock - > state ) ;
2017-03-16 22:18:50 -08:00
do {
2023-06-16 18:24:05 -04:00
ret = ! ( old & l [ type ] . lock_fail ) ;
2023-05-22 12:11:13 -04:00
if ( ! ret | | ( type = = SIX_LOCK_write & & ! try ) ) {
smp_mb ( ) ;
2023-05-20 20:37:53 -04:00
break ;
2023-05-22 12:11:13 -04:00
}
2023-05-25 14:35:06 -04:00
} while ( ! atomic_try_cmpxchg_acquire ( & lock - > state , & old , old + l [ type ] . lock_val ) ) ;
2017-03-16 22:18:50 -08:00
2023-05-22 00:17:40 -04:00
EBUG_ON ( ret & & ! ( atomic_read ( & lock - > state ) & l [ type ] . held_mask ) ) ;
2017-03-16 22:18:50 -08:00
}
2022-08-26 19:22:24 -04:00
if ( ret > 0 )
six_set_owner ( lock , type , old , task ) ;
2017-03-16 22:18:50 -08:00
2023-06-16 18:24:05 -04:00
EBUG_ON ( type = = SIX_LOCK_write & & try & & ret < = 0 & &
2023-05-22 12:11:13 -04:00
( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_write ) ) ;
2017-03-16 22:18:50 -08:00
return ret ;
}
2023-05-20 23:57:48 -04:00
static void __six_lock_wakeup ( struct six_lock * lock , enum six_lock_type lock_type )
2022-08-26 19:22:24 -04:00
{
struct six_lock_waiter * w , * next ;
struct task_struct * task ;
bool saw_one ;
int ret ;
again :
ret = 0 ;
saw_one = false ;
raw_spin_lock ( & lock - > wait_lock ) ;
list_for_each_entry_safe ( w , next , & lock - > wait_list , list ) {
if ( w - > lock_want ! = lock_type )
continue ;
if ( saw_one & & lock_type ! = SIX_LOCK_read )
goto unlock ;
saw_one = true ;
2023-05-21 15:40:40 -04:00
ret = __do_six_trylock ( lock , lock_type , w - > task , false ) ;
2022-08-26 19:22:24 -04:00
if ( ret < = 0 )
goto unlock ;
2023-08-12 17:10:42 -04:00
/*
* Similar to percpu_rwsem_wake_function ( ) , we need to guard
* against the wakee noticing w - > lock_acquired , returning , and
* then exiting before we do the wakeup :
*/
task = get_task_struct ( w - > task ) ;
2023-08-12 15:05:06 -04:00
__list_del ( w - > list . prev , w - > list . next ) ;
2022-08-26 19:22:24 -04:00
/*
2023-08-12 15:05:06 -04:00
* The release barrier here ensures the ordering of the
* __list_del before setting w - > lock_acquired ; @ w is on the
* stack of the thread doing the waiting and will be reused
* after it sees w - > lock_acquired with no other locking :
* pairs with smp_load_acquire ( ) in six_lock_slowpath ( )
2022-08-26 19:22:24 -04:00
*/
2023-08-12 15:05:06 -04:00
smp_store_release ( & w - > lock_acquired , true ) ;
2022-08-26 19:22:24 -04:00
wake_up_process ( task ) ;
2023-08-12 17:10:42 -04:00
put_task_struct ( task ) ;
2022-08-26 19:22:24 -04:00
}
2023-05-22 12:11:13 -04:00
six_clear_bitmask ( lock , SIX_LOCK_WAITING_read < < lock_type ) ;
2022-08-26 19:22:24 -04:00
unlock :
raw_spin_unlock ( & lock - > wait_lock ) ;
if ( ret < 0 ) {
lock_type = - ret - 1 ;
goto again ;
}
}
2023-05-20 21:44:30 -04:00
__always_inline
2023-05-22 00:17:40 -04:00
static void six_lock_wakeup ( struct six_lock * lock , u32 state ,
2023-05-20 21:44:30 -04:00
enum six_lock_type lock_type )
2022-08-26 19:22:24 -04:00
{
2023-05-20 23:57:48 -04:00
if ( lock_type = = SIX_LOCK_write & & ( state & SIX_LOCK_HELD_read ) )
2022-08-26 19:22:24 -04:00
return ;
2023-05-22 12:11:13 -04:00
if ( ! ( state & ( SIX_LOCK_WAITING_read < < lock_type ) ) )
2022-08-26 19:22:24 -04:00
return ;
__six_lock_wakeup ( lock , lock_type ) ;
}
2023-05-20 21:44:30 -04:00
__always_inline
2023-05-21 15:40:40 -04:00
static bool do_six_trylock ( struct six_lock * lock , enum six_lock_type type , bool try )
2022-08-26 19:22:24 -04:00
{
int ret ;
2023-05-21 15:40:40 -04:00
ret = __do_six_trylock ( lock , type , current , try ) ;
2022-08-26 19:22:24 -04:00
if ( ret < 0 )
__six_lock_wakeup ( lock , - ret - 1 ) ;
return ret > 0 ;
}
2023-05-21 15:40:40 -04:00
/**
* six_trylock_ip - attempt to take a six lock without blocking
* @ lock : lock to take
* @ type : SIX_LOCK_read , SIX_LOCK_intent , or SIX_LOCK_write
* @ ip : ip parameter for lockdep / lockstat , i . e . _THIS_IP_
*
* Return : true on success , false on failure .
*/
bool six_trylock_ip ( struct six_lock * lock , enum six_lock_type type , unsigned long ip )
2017-03-16 22:18:50 -08:00
{
2023-05-21 15:40:40 -04:00
if ( ! do_six_trylock ( lock , type , true ) )
2017-03-16 22:18:50 -08:00
return false ;
if ( type ! = SIX_LOCK_write )
2023-02-04 19:38:43 -05:00
six_acquire ( & lock - > dep_map , 1 , type = = SIX_LOCK_read , ip ) ;
2017-03-16 22:18:50 -08:00
return true ;
}
2023-05-21 15:40:40 -04:00
EXPORT_SYMBOL_GPL ( six_trylock_ip ) ;
/**
* six_relock_ip - attempt to re - take a lock that was held previously
* @ lock : lock to take
* @ type : SIX_LOCK_read , SIX_LOCK_intent , or SIX_LOCK_write
* @ seq : lock sequence number obtained from six_lock_seq ( ) while lock was
* held previously
* @ ip : ip parameter for lockdep / lockstat , i . e . _THIS_IP_
*
* Return : true on success , false on failure .
*/
bool six_relock_ip ( struct six_lock * lock , enum six_lock_type type ,
unsigned seq , unsigned long ip )
2017-03-16 22:18:50 -08:00
{
2023-06-16 15:00:48 -04:00
if ( six_lock_seq ( lock ) ! = seq | | ! six_trylock_ip ( lock , type , ip ) )
return false ;
2017-03-16 22:18:50 -08:00
2023-06-16 15:00:48 -04:00
if ( six_lock_seq ( lock ) ! = seq ) {
six_unlock_ip ( lock , type , ip ) ;
return false ;
2017-03-16 22:18:50 -08:00
}
return true ;
}
2023-05-21 15:40:40 -04:00
EXPORT_SYMBOL_GPL ( six_relock_ip ) ;
2017-03-16 22:18:50 -08:00
# ifdef CONFIG_SIX_LOCK_SPIN_ON_OWNER
2023-02-05 14:09:30 -05:00
static inline bool six_can_spin_on_owner ( struct six_lock * lock )
2017-03-16 22:18:50 -08:00
{
struct task_struct * owner ;
2023-02-05 14:09:30 -05:00
bool ret ;
2017-03-16 22:18:50 -08:00
if ( need_resched ( ) )
2023-02-05 14:09:30 -05:00
return false ;
2017-03-16 22:18:50 -08:00
rcu_read_lock ( ) ;
owner = READ_ONCE ( lock - > owner ) ;
2023-02-05 14:09:30 -05:00
ret = ! owner | | owner_on_cpu ( owner ) ;
2017-03-16 22:18:50 -08:00
rcu_read_unlock ( ) ;
2023-02-05 14:09:30 -05:00
return ret ;
}
2017-03-16 22:18:50 -08:00
static inline bool six_spin_on_owner ( struct six_lock * lock ,
2023-02-05 14:09:30 -05:00
struct task_struct * owner ,
u64 end_time )
2017-03-16 22:18:50 -08:00
{
bool ret = true ;
2023-02-05 14:09:30 -05:00
unsigned loop = 0 ;
2017-03-16 22:18:50 -08:00
rcu_read_lock ( ) ;
while ( lock - > owner = = owner ) {
/*
* Ensure we emit the owner - > on_cpu , dereference _after_
* checking lock - > owner still matches owner . If that fails ,
* owner might point to freed memory . If it still matches ,
* the rcu_read_lock ( ) ensures the memory stays valid .
*/
barrier ( ) ;
2023-02-05 14:09:30 -05:00
if ( ! owner_on_cpu ( owner ) | | need_resched ( ) ) {
ret = false ;
break ;
}
if ( ! ( + + loop & 0xf ) & & ( time_after64 ( sched_clock ( ) , end_time ) ) ) {
2023-05-22 12:11:13 -04:00
six_set_bitmask ( lock , SIX_LOCK_NOSPIN ) ;
2017-03-16 22:18:50 -08:00
ret = false ;
break ;
}
cpu_relax ( ) ;
}
rcu_read_unlock ( ) ;
return ret ;
}
static inline bool six_optimistic_spin ( struct six_lock * lock , enum six_lock_type type )
{
struct task_struct * task = current ;
2023-02-05 14:09:30 -05:00
u64 end_time ;
2017-03-16 22:18:50 -08:00
if ( type = = SIX_LOCK_write )
return false ;
preempt_disable ( ) ;
if ( ! six_can_spin_on_owner ( lock ) )
goto fail ;
if ( ! osq_lock ( & lock - > osq ) )
goto fail ;
2023-02-05 14:09:30 -05:00
end_time = sched_clock ( ) + 10 * NSEC_PER_USEC ;
2017-03-16 22:18:50 -08:00
while ( 1 ) {
struct task_struct * owner ;
/*
* If there ' s an owner , wait for it to either
* release the lock or go to sleep .
*/
owner = READ_ONCE ( lock - > owner ) ;
2023-02-05 14:09:30 -05:00
if ( owner & & ! six_spin_on_owner ( lock , owner , end_time ) )
2017-03-16 22:18:50 -08:00
break ;
2023-05-21 15:40:40 -04:00
if ( do_six_trylock ( lock , type , false ) ) {
2017-03-16 22:18:50 -08:00
osq_unlock ( & lock - > osq ) ;
preempt_enable ( ) ;
return true ;
}
/*
* When there ' s no owner , we might have preempted between the
* owner acquiring the lock and setting the owner field . If
* we ' re an RT task that will live - lock because we won ' t let
* the owner complete .
*/
if ( ! owner & & ( need_resched ( ) | | rt_task ( task ) ) )
break ;
/*
* The cpu_relax ( ) call is a compiler barrier which forces
* everything in this loop to be re - loaded . We don ' t need
* memory barriers as we ' ll eventually observe the right
* values at the cost of a few extra spins .
*/
cpu_relax ( ) ;
}
osq_unlock ( & lock - > osq ) ;
fail :
preempt_enable ( ) ;
/*
* If we fell out of the spin path because of need_resched ( ) ,
* reschedule now , before we try - lock again . This avoids getting
* scheduled out right after we obtained the lock .
*/
if ( need_resched ( ) )
schedule ( ) ;
return false ;
}
# else /* CONFIG_SIX_LOCK_SPIN_ON_OWNER */
static inline bool six_optimistic_spin ( struct six_lock * lock , enum six_lock_type type )
{
return false ;
}
# endif
noinline
2023-05-21 15:40:40 -04:00
static int six_lock_slowpath ( struct six_lock * lock , enum six_lock_type type ,
struct six_lock_waiter * wait ,
six_lock_should_sleep_fn should_sleep_fn , void * p ,
unsigned long ip )
2017-03-16 22:18:50 -08:00
{
int ret = 0 ;
if ( type = = SIX_LOCK_write ) {
2023-05-22 12:11:13 -04:00
EBUG_ON ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_write ) ;
atomic_add ( SIX_LOCK_HELD_write , & lock - > state ) ;
2017-03-16 22:18:50 -08:00
smp_mb__after_atomic ( ) ;
}
if ( six_optimistic_spin ( lock , type ) )
2022-08-26 19:22:24 -04:00
goto out ;
2017-03-16 22:18:50 -08:00
2023-02-04 19:38:43 -05:00
lock_contended ( & lock - > dep_map , ip ) ;
2017-03-16 22:18:50 -08:00
2022-08-27 16:22:51 -04:00
wait - > task = current ;
wait - > lock_want = type ;
2022-08-26 19:22:24 -04:00
wait - > lock_acquired = false ;
2022-08-25 10:49:52 -04:00
raw_spin_lock ( & lock - > wait_lock ) ;
2023-05-22 12:11:13 -04:00
six_set_bitmask ( lock , SIX_LOCK_WAITING_read < < type ) ;
2022-08-26 19:22:24 -04:00
/*
2023-05-21 15:40:40 -04:00
* Retry taking the lock after taking waitlist lock , in case we raced
* with an unlock :
2022-08-26 19:22:24 -04:00
*/
2023-05-21 15:40:40 -04:00
ret = __do_six_trylock ( lock , type , current , false ) ;
2022-08-26 19:22:24 -04:00
if ( ret < = 0 ) {
wait - > start_time = local_clock ( ) ;
2022-09-24 01:33:13 -04:00
2022-08-26 19:22:24 -04:00
if ( ! list_empty ( & lock - > wait_list ) ) {
struct six_lock_waiter * last =
list_last_entry ( & lock - > wait_list ,
struct six_lock_waiter , list ) ;
2022-09-24 01:33:13 -04:00
2022-08-26 19:22:24 -04:00
if ( time_before_eq64 ( wait - > start_time , last - > start_time ) )
wait - > start_time = last - > start_time + 1 ;
}
2022-09-24 01:33:13 -04:00
2022-08-26 19:22:24 -04:00
list_add_tail ( & wait - > list , & lock - > wait_list ) ;
}
2022-08-25 10:49:52 -04:00
raw_spin_unlock ( & lock - > wait_lock ) ;
2017-03-16 22:18:50 -08:00
2022-08-26 19:22:24 -04:00
if ( unlikely ( ret > 0 ) ) {
ret = 0 ;
goto out ;
}
if ( unlikely ( ret < 0 ) ) {
__six_lock_wakeup ( lock , - ret - 1 ) ;
ret = 0 ;
}
2017-03-16 22:18:50 -08:00
while ( 1 ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
2022-08-26 19:22:24 -04:00
2023-08-12 15:05:06 -04:00
/*
* Ensures that writes to the waitlist entry happen after we see
* wait - > lock_acquired : pairs with the smp_store_release in
* __six_lock_wakeup
*/
if ( smp_load_acquire ( & wait - > lock_acquired ) )
2017-03-16 22:18:50 -08:00
break ;
ret = should_sleep_fn ? should_sleep_fn ( lock , p ) : 0 ;
2022-08-26 19:22:24 -04:00
if ( unlikely ( ret ) ) {
2023-08-12 15:05:06 -04:00
bool acquired ;
/*
* If should_sleep_fn ( ) returns an error , we are
* required to return that error even if we already
* acquired the lock - should_sleep_fn ( ) might have
* modified external state ( e . g . when the deadlock cycle
* detector in bcachefs issued a transaction restart )
*/
2022-08-26 19:22:24 -04:00
raw_spin_lock ( & lock - > wait_lock ) ;
2023-08-12 15:05:06 -04:00
acquired = wait - > lock_acquired ;
if ( ! acquired )
2022-08-26 19:22:24 -04:00
list_del ( & wait - > list ) ;
raw_spin_unlock ( & lock - > wait_lock ) ;
2023-08-12 15:05:06 -04:00
if ( unlikely ( acquired ) )
2022-08-26 19:22:24 -04:00
do_six_unlock_type ( lock , type ) ;
2017-03-16 22:18:50 -08:00
break ;
2022-08-26 19:22:24 -04:00
}
2017-03-16 22:18:50 -08:00
schedule ( ) ;
}
__set_current_state ( TASK_RUNNING ) ;
2022-08-26 19:22:24 -04:00
out :
2023-05-20 23:57:48 -04:00
if ( ret & & type = = SIX_LOCK_write ) {
2023-05-22 12:11:13 -04:00
six_clear_bitmask ( lock , SIX_LOCK_HELD_write ) ;
2023-05-25 18:10:04 -04:00
six_lock_wakeup ( lock , atomic_read ( & lock - > state ) , SIX_LOCK_read ) ;
2017-03-16 22:18:50 -08:00
}
return ret ;
}
2023-05-21 15:40:40 -04:00
/**
* six_lock_ip_waiter - take a lock , with full waitlist interface
* @ lock : lock to take
* @ type : SIX_LOCK_read , SIX_LOCK_intent , or SIX_LOCK_write
* @ wait : pointer to wait object , which will be added to lock ' s waitlist
* @ should_sleep_fn : callback run after adding to waitlist , immediately prior
* to scheduling
* @ p : passed through to @ should_sleep_fn
* @ ip : ip parameter for lockdep / lockstat , i . e . _THIS_IP_
*
* This is the most general six_lock ( ) variant , with parameters to support full
* cycle detection for deadlock avoidance .
*
* The code calling this function must implement tracking of held locks , and the
* @ wait object should be embedded into the struct that tracks held locks -
* which must also be accessible in a thread - safe way .
*
* @ should_sleep_fn should invoke the cycle detector ; it should walk each
* lock ' s waiters , and for each waiter recursively walk their held locks .
*
* When this function must block , @ wait will be added to @ lock ' s waitlist before
* calling trylock , and before calling @ should_sleep_fn , and @ wait will not be
* removed from the lock waitlist until the lock has been successfully acquired ,
* or we abort .
*
* @ wait . start_time will be monotonically increasing for any given waitlist , and
* thus may be used as a loop cursor .
*
* Return : 0 on success , or the return code from @ should_sleep_fn on failure .
*/
int six_lock_ip_waiter ( struct six_lock * lock , enum six_lock_type type ,
struct six_lock_waiter * wait ,
six_lock_should_sleep_fn should_sleep_fn , void * p ,
unsigned long ip )
2017-03-16 22:18:50 -08:00
{
int ret ;
2022-09-24 01:33:13 -04:00
wait - > start_time = 0 ;
2017-03-16 22:18:50 -08:00
if ( type ! = SIX_LOCK_write )
2023-02-04 19:38:43 -05:00
six_acquire ( & lock - > dep_map , 0 , type = = SIX_LOCK_read , ip ) ;
2017-03-16 22:18:50 -08:00
2023-05-21 15:40:40 -04:00
ret = do_six_trylock ( lock , type , true ) ? 0
: six_lock_slowpath ( lock , type , wait , should_sleep_fn , p , ip ) ;
2017-03-16 22:18:50 -08:00
if ( ret & & type ! = SIX_LOCK_write )
2023-02-04 19:38:43 -05:00
six_release ( & lock - > dep_map , ip ) ;
2017-03-16 22:18:50 -08:00
if ( ! ret )
2023-02-04 19:38:43 -05:00
lock_acquired ( & lock - > dep_map , ip ) ;
2017-03-16 22:18:50 -08:00
return ret ;
}
2023-05-21 15:40:40 -04:00
EXPORT_SYMBOL_GPL ( six_lock_ip_waiter ) ;
2017-03-16 22:18:50 -08:00
2022-08-27 16:22:51 -04:00
__always_inline
2022-08-26 19:22:24 -04:00
static void do_six_unlock_type ( struct six_lock * lock , enum six_lock_type type )
2017-03-16 22:18:50 -08:00
{
2023-05-22 00:17:40 -04:00
u32 state ;
2017-03-16 22:18:50 -08:00
2022-08-26 19:22:24 -04:00
if ( type = = SIX_LOCK_intent )
2017-03-16 22:18:50 -08:00
lock - > owner = NULL ;
if ( type = = SIX_LOCK_read & &
lock - > readers ) {
smp_mb ( ) ; /* unlock barrier */
this_cpu_dec ( * lock - > readers ) ;
smp_mb ( ) ; /* between unlocking and checking for waiters */
2023-05-22 00:17:40 -04:00
state = atomic_read ( & lock - > state ) ;
2017-03-16 22:18:50 -08:00
} else {
2023-05-22 00:17:40 -04:00
u32 v = l [ type ] . lock_val ;
2023-02-05 14:09:30 -05:00
if ( type ! = SIX_LOCK_read )
2023-05-22 12:11:13 -04:00
v + = atomic_read ( & lock - > state ) & SIX_LOCK_NOSPIN ;
2023-02-05 14:09:30 -05:00
2023-05-22 00:17:40 -04:00
EBUG_ON ( ! ( atomic_read ( & lock - > state ) & l [ type ] . held_mask ) ) ;
state = atomic_sub_return_release ( v , & lock - > state ) ;
2017-03-16 22:18:50 -08:00
}
six_lock_wakeup ( lock , state , l [ type ] . unlock_wakeup ) ;
}
2023-05-21 15:40:40 -04:00
/**
* six_unlock_ip - drop a six lock
* @ lock : lock to unlock
* @ type : SIX_LOCK_read , SIX_LOCK_intent , or SIX_LOCK_write
* @ ip : ip parameter for lockdep / lockstat , i . e . _THIS_IP_
*
* When a lock is held multiple times ( because six_lock_incement ( ) ) was used ) ,
* this decrements the ' lock held ' counter by one .
*
* For example :
* six_lock_read ( & foo - > lock ) ; read count 1
* six_lock_increment ( & foo - > lock , SIX_LOCK_read ) ; read count 2
* six_lock_unlock ( & foo - > lock , SIX_LOCK_read ) ; read count 1
* six_lock_unlock ( & foo - > lock , SIX_LOCK_read ) ; read count 0
*/
void six_unlock_ip ( struct six_lock * lock , enum six_lock_type type , unsigned long ip )
2022-08-26 19:22:24 -04:00
{
EBUG_ON ( type = = SIX_LOCK_write & &
2023-05-22 00:17:40 -04:00
! ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_intent ) ) ;
2022-08-26 19:22:24 -04:00
EBUG_ON ( ( type = = SIX_LOCK_write | |
type = = SIX_LOCK_intent ) & &
lock - > owner ! = current ) ;
if ( type ! = SIX_LOCK_write )
2023-02-04 19:38:43 -05:00
six_release ( & lock - > dep_map , ip ) ;
2023-06-16 18:24:05 -04:00
else
2023-05-22 00:17:40 -04:00
lock - > seq + + ;
2022-08-26 19:22:24 -04:00
if ( type = = SIX_LOCK_intent & &
lock - > intent_lock_recurse ) {
- - lock - > intent_lock_recurse ;
return ;
}
do_six_unlock_type ( lock , type ) ;
}
2023-05-21 15:40:40 -04:00
EXPORT_SYMBOL_GPL ( six_unlock_ip ) ;
2017-03-16 22:18:50 -08:00
2023-05-21 15:40:40 -04:00
/**
* six_lock_downgrade - convert an intent lock to a read lock
* @ lock : lock to dowgrade
*
* @ lock will have read count incremented and intent count decremented
*/
2017-03-16 22:18:50 -08:00
void six_lock_downgrade ( struct six_lock * lock )
{
six_lock_increment ( lock , SIX_LOCK_read ) ;
six_unlock_intent ( lock ) ;
}
EXPORT_SYMBOL_GPL ( six_lock_downgrade ) ;
2023-05-21 15:40:40 -04:00
/**
* six_lock_tryupgrade - attempt to convert read lock to an intent lock
* @ lock : lock to upgrade
*
* On success , @ lock will have intent count incremented and read count
* decremented
*
* Return : true on success , false on failure
*/
2017-03-16 22:18:50 -08:00
bool six_lock_tryupgrade ( struct six_lock * lock )
{
2023-05-25 14:35:06 -04:00
u32 old = atomic_read ( & lock - > state ) , new ;
2017-03-16 22:18:50 -08:00
do {
2023-05-25 14:35:06 -04:00
new = old ;
2017-03-16 22:18:50 -08:00
2023-05-20 23:57:48 -04:00
if ( new & SIX_LOCK_HELD_intent )
2017-03-16 22:18:50 -08:00
return false ;
if ( ! lock - > readers ) {
2023-05-20 23:57:48 -04:00
EBUG_ON ( ! ( new & SIX_LOCK_HELD_read ) ) ;
2023-06-16 18:24:05 -04:00
new - = l [ SIX_LOCK_read ] . lock_val ;
2017-03-16 22:18:50 -08:00
}
2023-05-20 23:57:48 -04:00
new | = SIX_LOCK_HELD_intent ;
2023-05-25 14:35:06 -04:00
} while ( ! atomic_try_cmpxchg_acquire ( & lock - > state , & old , new ) ) ;
2017-03-16 22:18:50 -08:00
if ( lock - > readers )
this_cpu_dec ( * lock - > readers ) ;
2022-08-26 19:22:24 -04:00
six_set_owner ( lock , SIX_LOCK_intent , old , current ) ;
2017-03-16 22:18:50 -08:00
return true ;
}
EXPORT_SYMBOL_GPL ( six_lock_tryupgrade ) ;
2023-05-21 15:40:40 -04:00
/**
* six_trylock_convert - attempt to convert a held lock from one type to another
* @ lock : lock to upgrade
* @ from : SIX_LOCK_read or SIX_LOCK_intent
* @ to : SIX_LOCK_read or SIX_LOCK_intent
*
* On success , @ lock will have intent count incremented and read count
* decremented
*
* Return : true on success , false on failure
*/
2017-03-16 22:18:50 -08:00
bool six_trylock_convert ( struct six_lock * lock ,
enum six_lock_type from ,
enum six_lock_type to )
{
EBUG_ON ( to = = SIX_LOCK_write | | from = = SIX_LOCK_write ) ;
if ( to = = from )
return true ;
if ( to = = SIX_LOCK_read ) {
six_lock_downgrade ( lock ) ;
return true ;
} else {
return six_lock_tryupgrade ( lock ) ;
}
}
EXPORT_SYMBOL_GPL ( six_trylock_convert ) ;
2023-05-21 15:40:40 -04:00
/**
* six_lock_increment - increase held lock count on a lock that is already held
* @ lock : lock to increment
* @ type : SIX_LOCK_read or SIX_LOCK_intent
*
* @ lock must already be held , with a lock type that is greater than or equal to
* @ type
*
* A corresponding six_unlock_type ( ) call will be required for @ lock to be fully
* unlocked .
2017-03-16 22:18:50 -08:00
*/
void six_lock_increment ( struct six_lock * lock , enum six_lock_type type )
{
2023-02-04 19:38:43 -05:00
six_acquire ( & lock - > dep_map , 0 , type = = SIX_LOCK_read , _RET_IP_ ) ;
2017-03-16 22:18:50 -08:00
/* XXX: assert already locked, and that we don't overflow: */
switch ( type ) {
case SIX_LOCK_read :
if ( lock - > readers ) {
this_cpu_inc ( * lock - > readers ) ;
} else {
2023-05-22 00:17:40 -04:00
EBUG_ON ( ! ( atomic_read ( & lock - > state ) &
2023-05-20 23:57:48 -04:00
( SIX_LOCK_HELD_read |
SIX_LOCK_HELD_intent ) ) ) ;
2023-05-22 00:17:40 -04:00
atomic_add ( l [ type ] . lock_val , & lock - > state ) ;
2017-03-16 22:18:50 -08:00
}
break ;
case SIX_LOCK_intent :
2023-05-22 00:17:40 -04:00
EBUG_ON ( ! ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_intent ) ) ;
2017-03-16 22:18:50 -08:00
lock - > intent_lock_recurse + + ;
break ;
case SIX_LOCK_write :
BUG ( ) ;
break ;
}
}
EXPORT_SYMBOL_GPL ( six_lock_increment ) ;
2023-05-21 15:40:40 -04:00
/**
* six_lock_wakeup_all - wake up all waiters on @ lock
* @ lock : lock to wake up waiters for
*
* Wakeing up waiters will cause them to re - run should_sleep_fn , which may then
* abort the lock operation .
*
* This function is never needed in a bug - free program ; it ' s only useful in
* debug code , e . g . to determine if a cycle detector is at fault .
*/
2017-03-16 22:18:50 -08:00
void six_lock_wakeup_all ( struct six_lock * lock )
{
2023-05-22 00:17:40 -04:00
u32 state = atomic_read ( & lock - > state ) ;
2017-03-16 22:18:50 -08:00
struct six_lock_waiter * w ;
2022-08-26 19:22:24 -04:00
six_lock_wakeup ( lock , state , SIX_LOCK_read ) ;
six_lock_wakeup ( lock , state , SIX_LOCK_intent ) ;
six_lock_wakeup ( lock , state , SIX_LOCK_write ) ;
2017-03-16 22:18:50 -08:00
raw_spin_lock ( & lock - > wait_lock ) ;
2022-08-25 10:49:52 -04:00
list_for_each_entry ( w , & lock - > wait_list , list )
2017-03-16 22:18:50 -08:00
wake_up_process ( w - > task ) ;
raw_spin_unlock ( & lock - > wait_lock ) ;
}
EXPORT_SYMBOL_GPL ( six_lock_wakeup_all ) ;
2023-05-21 15:40:40 -04:00
/**
* six_lock_counts - return held lock counts , for each lock type
* @ lock : lock to return counters for
*
* Return : the number of times a lock is held for read , intent and write .
2017-03-16 22:18:50 -08:00
*/
struct six_lock_count six_lock_counts ( struct six_lock * lock )
{
2022-08-21 23:08:53 -04:00
struct six_lock_count ret ;
2023-02-15 18:29:16 -05:00
ret . n [ SIX_LOCK_read ] = ! lock - > readers
2023-05-22 12:11:13 -04:00
? atomic_read ( & lock - > state ) & SIX_LOCK_HELD_read
2023-02-15 18:29:16 -05:00
: pcpu_read_count ( lock ) ;
2023-05-22 00:17:40 -04:00
ret . n [ SIX_LOCK_intent ] = ! ! ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_intent ) +
2023-05-20 23:57:48 -04:00
lock - > intent_lock_recurse ;
2023-05-22 00:17:40 -04:00
ret . n [ SIX_LOCK_write ] = ! ! ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_write ) ;
2017-03-16 22:18:50 -08:00
return ret ;
}
EXPORT_SYMBOL_GPL ( six_lock_counts ) ;
2023-05-20 20:40:08 -04:00
2023-05-21 15:40:40 -04:00
/**
* six_lock_readers_add - directly manipulate reader count of a lock
* @ lock : lock to add / subtract readers for
* @ nr : reader count to add / subtract
*
* When an upper layer is implementing lock reentrency , we may have both read
* and intent locks on the same lock .
*
* When we need to take a write lock , the read locks will cause self - deadlock ,
* because six locks themselves do not track which read locks are held by the
* current thread and which are held by a different thread - it does no
* per - thread tracking of held locks .
*
* The upper layer that is tracking held locks may however , if trylock ( ) has
* failed , count up its own read locks , subtract them , take the write lock , and
* then re - add them .
*
* As in any other situation when taking a write lock , @ lock must be held for
* intent one ( or more ) times , so @ lock will never be left unlocked .
*/
2023-05-20 20:40:08 -04:00
void six_lock_readers_add ( struct six_lock * lock , int nr )
{
2023-05-21 15:40:40 -04:00
if ( lock - > readers ) {
2023-05-20 20:40:08 -04:00
this_cpu_add ( * lock - > readers , nr ) ;
2023-05-21 15:40:40 -04:00
} else {
2023-05-22 12:11:13 -04:00
EBUG_ON ( ( int ) ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_read ) + nr < 0 ) ;
2023-05-21 15:40:40 -04:00
/* reader count starts at bit 0 */
2023-05-22 00:17:40 -04:00
atomic_add ( nr , & lock - > state ) ;
2023-05-21 15:40:40 -04:00
}
2023-05-20 20:40:08 -04:00
}
EXPORT_SYMBOL_GPL ( six_lock_readers_add ) ;
2023-05-20 20:57:55 -04:00
2023-05-21 15:40:40 -04:00
/**
* six_lock_exit - release resources held by a lock prior to freeing
* @ lock : lock to exit
*
* When a lock was initialized in percpu mode ( SIX_OLCK_INIT_PCPU ) , this is
* required to free the percpu read counts .
*/
2023-05-20 20:57:55 -04:00
void six_lock_exit ( struct six_lock * lock )
{
WARN_ON ( lock - > readers & & pcpu_read_count ( lock ) ) ;
2023-05-22 00:17:40 -04:00
WARN_ON ( atomic_read ( & lock - > state ) & SIX_LOCK_HELD_read ) ;
2023-05-20 20:57:55 -04:00
free_percpu ( lock - > readers ) ;
lock - > readers = NULL ;
}
EXPORT_SYMBOL_GPL ( six_lock_exit ) ;
void __six_lock_init ( struct six_lock * lock , const char * name ,
struct lock_class_key * key , enum six_lock_init_flags flags )
{
2023-05-22 00:17:40 -04:00
atomic_set ( & lock - > state , 0 ) ;
2023-05-20 20:57:55 -04:00
raw_spin_lock_init ( & lock - > wait_lock ) ;
INIT_LIST_HEAD ( & lock - > wait_list ) ;
# ifdef CONFIG_DEBUG_LOCK_ALLOC
debug_check_no_locks_freed ( ( void * ) lock , sizeof ( * lock ) ) ;
lockdep_init_map ( & lock - > dep_map , name , key , 0 ) ;
# endif
2023-06-10 10:57:23 -04:00
/*
* Don ' t assume that we have real percpu variables available in
* userspace :
*/
# ifdef __KERNEL__
2023-05-20 20:57:55 -04:00
if ( flags & SIX_LOCK_INIT_PCPU ) {
/*
* We don ' t return an error here on memory allocation failure
* since percpu is an optimization , and locks will work with the
* same semantics in non - percpu mode : callers can check for
* failure if they wish by checking lock - > readers , but generally
* will not want to treat it as an error .
*/
lock - > readers = alloc_percpu ( unsigned ) ;
}
2023-06-10 10:57:23 -04:00
# endif
2023-05-20 20:57:55 -04:00
}
EXPORT_SYMBOL_GPL ( __six_lock_init ) ;