Skip to content

Commit 6bbfcf0

Browse files
committed
locking/rwsem: Disable preemption in all down_read*() and up_read() code paths
jira LE-1907 Rebuild_History Non-Buildable kernel-rt-5.14.0-284.30.1.rt14.315.el9_2 commit-author Waiman Long <longman@redhat.com> commit 3f52455 Commit: 91d2a81 ("locking/rwsem: Make handoff writer optimistically spin on owner") ... assumes that when the owner field is changed to NULL, the lock will become free soon. But commit: 48dfb5d ("locking/rwsem: Disable preemption while trying for rwsem lock") ... disabled preemption when acquiring rwsem for write. However, preemption has not yet been disabled when acquiring a read lock on a rwsem. So a reader can add a RWSEM_READER_BIAS to count without setting owner to signal a reader, got preempted out by a RT task which then spins in the writer slowpath as owner remains NULL leading to live lock. One easy way to fix this problem is to disable preemption at all the down_read*() and up_read() code paths as implemented in this patch. Fixes: 91d2a81 ("locking/rwsem: Make handoff writer optimistically spin on owner") Reported-by: Mukesh Ojha <quic_mojha@quicinc.com> Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Waiman Long <longman@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://lore.kernel.org/r/20230126003628.365092-3-longman@redhat.com (cherry picked from commit 3f52455) Signed-off-by: Jonathan Maple <jmaple@ciq.com>
1 parent 80bad6f commit 6bbfcf0

File tree

1 file changed

+24
-6
lines changed

1 file changed

+24
-6
lines changed

kernel/locking/rwsem.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,7 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat
10041004
/* Ordered by sem->wait_lock against rwsem_mark_wake(). */
10051005
break;
10061006
}
1007-
schedule();
1007+
schedule_preempt_disabled();
10081008
lockevent_inc(rwsem_sleep_reader);
10091009
}
10101010

@@ -1213,14 +1213,20 @@ static struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem)
12131213
*/
12141214
static inline int __down_read_common(struct rw_semaphore *sem, int state)
12151215
{
1216+
int ret = 0;
12161217
long count;
12171218

1219+
preempt_disable();
12181220
if (!rwsem_read_trylock(sem, &count)) {
1219-
if (IS_ERR(rwsem_down_read_slowpath(sem, count, state)))
1220-
return -EINTR;
1221+
if (IS_ERR(rwsem_down_read_slowpath(sem, count, state))) {
1222+
ret = -EINTR;
1223+
goto out;
1224+
}
12211225
DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
12221226
}
1223-
return 0;
1227+
out:
1228+
preempt_enable();
1229+
return ret;
12241230
}
12251231

12261232
static inline void __down_read(struct rw_semaphore *sem)
@@ -1240,19 +1246,23 @@ static inline int __down_read_killable(struct rw_semaphore *sem)
12401246

12411247
static inline int __down_read_trylock(struct rw_semaphore *sem)
12421248
{
1249+
int ret = 0;
12431250
long tmp;
12441251

12451252
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
12461253

1254+
preempt_disable();
12471255
tmp = atomic_long_read(&sem->count);
12481256
while (!(tmp & RWSEM_READ_FAILED_MASK)) {
12491257
if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,
12501258
tmp + RWSEM_READER_BIAS)) {
12511259
rwsem_set_reader_owned(sem);
1252-
return 1;
1260+
ret = 1;
1261+
break;
12531262
}
12541263
}
1255-
return 0;
1264+
preempt_enable();
1265+
return ret;
12561266
}
12571267

12581268
/*
@@ -1294,6 +1304,7 @@ static inline void __up_read(struct rw_semaphore *sem)
12941304
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
12951305
DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
12961306

1307+
preempt_disable();
12971308
rwsem_clear_reader_owned(sem);
12981309
tmp = atomic_long_add_return_release(-RWSEM_READER_BIAS, &sem->count);
12991310
DEBUG_RWSEMS_WARN_ON(tmp < 0, sem);
@@ -1302,6 +1313,7 @@ static inline void __up_read(struct rw_semaphore *sem)
13021313
clear_nonspinnable(sem);
13031314
rwsem_wake(sem);
13041315
}
1316+
preempt_enable();
13051317
}
13061318

13071319
/*
@@ -1621,6 +1633,12 @@ void down_read_non_owner(struct rw_semaphore *sem)
16211633
{
16221634
might_sleep();
16231635
__down_read(sem);
1636+
/*
1637+
* The owner value for a reader-owned lock is mostly for debugging
1638+
* purpose only and is not critical to the correct functioning of
1639+
* rwsem. So it is perfectly fine to set it in a preempt-enabled
1640+
* context here.
1641+
*/
16241642
__rwsem_set_reader_owned(sem, NULL);
16251643
}
16261644
EXPORT_SYMBOL(down_read_non_owner);

0 commit comments

Comments
 (0)