Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

locking/percpu-rwsem: add freezable alternative to down_read

Percpu-rwsems are used for superblock locking. However, we know the
read percpu-rwsem we take for sb_start_write() on a frozen filesystem
needs not to inhibit system from suspending or hibernating. That
means it needs to wait with TASK_UNINTERRUPTIBLE | TASK_FREEZABLE.

Introduce a new percpu_down_read_freezable() that allows us to control
whether TASK_FREEZABLE is added to the wait flags.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Link: https://lore.kernel.org/r/20250327140613.25178-2-James.Bottomley@HansenPartnership.com
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

James Bottomley and committed by
Christian Brauner
559b3bbf 0af2f6be

+24 -9
+16 -4
include/linux/percpu-rwsem.h
··· 43 43 #define DEFINE_STATIC_PERCPU_RWSEM(name) \ 44 44 __DEFINE_PERCPU_RWSEM(name, static) 45 45 46 - extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool); 46 + extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool, bool); 47 47 48 - static inline void percpu_down_read(struct percpu_rw_semaphore *sem) 48 + static inline void percpu_down_read_internal(struct percpu_rw_semaphore *sem, 49 + bool freezable) 49 50 { 50 51 might_sleep(); 51 52 ··· 64 63 if (likely(rcu_sync_is_idle(&sem->rss))) 65 64 this_cpu_inc(*sem->read_count); 66 65 else 67 - __percpu_down_read(sem, false); /* Unconditional memory barrier */ 66 + __percpu_down_read(sem, false, freezable); /* Unconditional memory barrier */ 68 67 /* 69 68 * The preempt_enable() prevents the compiler from 70 69 * bleeding the critical section out. 71 70 */ 72 71 preempt_enable(); 72 + } 73 + 74 + static inline void percpu_down_read(struct percpu_rw_semaphore *sem) 75 + { 76 + percpu_down_read_internal(sem, false); 77 + } 78 + 79 + static inline void percpu_down_read_freezable(struct percpu_rw_semaphore *sem, 80 + bool freeze) 81 + { 82 + percpu_down_read_internal(sem, freeze); 73 83 } 74 84 75 85 static inline bool percpu_down_read_trylock(struct percpu_rw_semaphore *sem) ··· 94 82 if (likely(rcu_sync_is_idle(&sem->rss))) 95 83 this_cpu_inc(*sem->read_count); 96 84 else 97 - ret = __percpu_down_read(sem, true); /* Unconditional memory barrier */ 85 + ret = __percpu_down_read(sem, true, false); /* Unconditional memory barrier */ 98 86 preempt_enable(); 99 87 /* 100 88 * The barrier() from preempt_enable() prevents the compiler from
+8 -5
kernel/locking/percpu-rwsem.c
··· 138 138 return !reader; /* wake (readers until) 1 writer */ 139 139 } 140 140 141 - static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader) 141 + static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader, 142 + bool freeze) 142 143 { 143 144 DEFINE_WAIT_FUNC(wq_entry, percpu_rwsem_wake_function); 144 145 bool wait; ··· 157 156 spin_unlock_irq(&sem->waiters.lock); 158 157 159 158 while (wait) { 160 - set_current_state(TASK_UNINTERRUPTIBLE); 159 + set_current_state(TASK_UNINTERRUPTIBLE | 160 + (freeze ? TASK_FREEZABLE : 0)); 161 161 if (!smp_load_acquire(&wq_entry.private)) 162 162 break; 163 163 schedule(); ··· 166 164 __set_current_state(TASK_RUNNING); 167 165 } 168 166 169 - bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) 167 + bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try, 168 + bool freeze) 170 169 { 171 170 if (__percpu_down_read_trylock(sem)) 172 171 return true; ··· 177 174 178 175 trace_contention_begin(sem, LCB_F_PERCPU | LCB_F_READ); 179 176 preempt_enable(); 180 - percpu_rwsem_wait(sem, /* .reader = */ true); 177 + percpu_rwsem_wait(sem, /* .reader = */ true, freeze); 181 178 preempt_disable(); 182 179 trace_contention_end(sem, 0); 183 180 ··· 240 237 */ 241 238 if (!__percpu_down_write_trylock(sem)) { 242 239 trace_contention_begin(sem, LCB_F_PERCPU | LCB_F_WRITE); 243 - percpu_rwsem_wait(sem, /* .reader = */ false); 240 + percpu_rwsem_wait(sem, /* .reader = */ false, false); 244 241 contended = true; 245 242 } 246 243