* [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation
@ 2023-10-24 12:08 Oleg Nesterov
2023-10-24 12:08 ` [RFC PATCH 2/2] seqlock: introduce need_seqretry_xxx() Oleg Nesterov
2023-11-16 14:45 ` [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation Oleg Nesterov
0 siblings, 2 replies; 4+ messages in thread
From: Oleg Nesterov @ 2023-10-24 12:08 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra
Cc: Alexey Gladkov, Ahmed S. Darwish, Boqun Feng, Jonathan Corbet,
Waiman Long, Will Deacon, linux-kernel, linux-doc
Half of the read_seqbegin_or_lock's users are buggy (I'll send the
fixes), and I guess this is because the documentation and the pseudo
code in Documentation/locking/seqlock.rst are wrong.
Pseudo code:
int seq = 0;
do {
read_seqbegin_or_lock(&foo_seqlock, &seq);
/* ... [[read-side critical section]] ... */
} while (need_seqretry(&foo_seqlock, seq));
read_seqbegin_or_lock() returns with the even seq, need_seqretry()
doesn't change this counter. This means that seq is always even and
thus the locking pass is simply impossible.
IOW, "_or_lock" has no effect and this code doesn't differ from
do {
seq = read_seqbegin(&foo_seqlock);
/* ... [[read-side critical section]] ... */
} while (read_seqretry(&foo_seqlock, seq));
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
Documentation/locking/seqlock.rst | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/Documentation/locking/seqlock.rst b/Documentation/locking/seqlock.rst
index bfda1a5fecad..4bdf8d4ed2a2 100644
--- a/Documentation/locking/seqlock.rst
+++ b/Documentation/locking/seqlock.rst
@@ -218,13 +218,14 @@ Read path, three categories:
according to a passed marker. This is used to avoid lockless readers
starvation (too much retry loops) in case of a sharp spike in write
activity. First, a lockless read is tried (even marker passed). If
- that trial fails (odd sequence counter is returned, which is used as
- the next iteration marker), the lockless read is transformed to a
- full locking read and no retry loop is necessary::
+ that trial fails (sequence counter doesn't match), make the marker
+ odd for the next iteration, the lockless read is transformed to a
+ full locking read and no retry loop is necessary, for example::
/* marker; even initialization */
- int seq = 0;
+ int seq = 1;
do {
+ seq++; /* 2 on the 1st/lockless path, otherwise odd */
read_seqbegin_or_lock(&foo_seqlock, &seq);
/* ... [[read-side critical section]] ... */
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 4+ messages in thread* [RFC PATCH 2/2] seqlock: introduce need_seqretry_xxx() 2023-10-24 12:08 [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation Oleg Nesterov @ 2023-10-24 12:08 ` Oleg Nesterov 2023-10-24 13:24 ` Oleg Nesterov 2023-11-16 14:45 ` [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation Oleg Nesterov 1 sibling, 1 reply; 4+ messages in thread From: Oleg Nesterov @ 2023-10-24 12:08 UTC (permalink / raw) To: Ingo Molnar, Peter Zijlstra Cc: Alexey Gladkov, Ahmed S. Darwish, Boqun Feng, Jonathan Corbet, Waiman Long, Will Deacon, linux-kernel, linux-doc Not for inclusion, just for discussion... Modulo naming, do you think the new need_seqretry_xxx() makes sense? Simpler to use and less error prone. thread_group_cputime() is changed as an example. --- include/linux/seqlock.h | 10 ++++++++++ kernel/sched/cputime.c | 9 +++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index d0050c889f26..9b3bc4ce3332 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -1165,6 +1165,16 @@ static inline int need_seqretry(seqlock_t *lock, int seq) return !(seq & 1) && read_seqretry(lock, seq); } +static inline int need_seqretry_xxx(seqlock_t *lock, int *seq) +{ + int ret = !(*seq & 1) && read_seqretry(lock, *seq); + + if (ret) + ++*seq; /* make this counter odd */ + + return ret; +} + /** * done_seqretry() - end seqlock_t "locking or lockless" reader section * @lock: Pointer to seqlock_t diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index af7952f12e6c..45704a84baec 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -314,7 +314,7 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times) struct signal_struct *sig = tsk->signal; u64 utime, stime; struct task_struct *t; - unsigned int seq, nextseq; + unsigned int seq; unsigned long flags; /* @@ -330,9 +330,8 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times) rcu_read_lock(); /* Attempt a lockless read on the first round. */ - nextseq = 0; + seq = 0; do { - seq = nextseq; flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq); times->utime = sig->utime; times->stime = sig->stime; @@ -344,9 +343,7 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times) times->stime += stime; times->sum_exec_runtime += read_sum_exec_runtime(t); } - /* If lockless access failed, take the lock. */ - nextseq = 1; - } while (need_seqretry(&sig->stats_lock, seq)); + } while (need_seqretry_xxx(&sig->stats_lock, &seq)); done_seqretry_irqrestore(&sig->stats_lock, seq, flags); rcu_read_unlock(); } -- 2.25.1.362.g51ebf55 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [RFC PATCH 2/2] seqlock: introduce need_seqretry_xxx() 2023-10-24 12:08 ` [RFC PATCH 2/2] seqlock: introduce need_seqretry_xxx() Oleg Nesterov @ 2023-10-24 13:24 ` Oleg Nesterov 0 siblings, 0 replies; 4+ messages in thread From: Oleg Nesterov @ 2023-10-24 13:24 UTC (permalink / raw) To: Ingo Molnar, Peter Zijlstra Cc: Alexey Gladkov, Ahmed S. Darwish, Boqun Feng, Jonathan Corbet, Waiman Long, Will Deacon, linux-kernel, linux-doc Or perhaps even something like static inline int xxx(seqlock_t *lock, int *seq, int lockless) { if (lockless) { *seq = read_seqbegin(lock); return 1; } if (*seq & 1) { read_sequnlock_excl(lock); return 0; } if (read_seqretry(lock, *seq)) { read_seqlock_excl(lock); *seq = 1; return 1; } return 0; } #define __XXX(lock, seq, lockless) \ for (int lockless = 1, seq; xxx(lock, &seq, lockless); lockless = 0) #define XXX(lock) \ __XXX(lock, __UNIQUE_ID(seq), __UNIQUE_ID(lockless)) ? This way one can do seqlock_t sl; void func(void) { XXX(&sl) { ... read-side critical section ... } } using the single XXX() helper, no need to declare/initialize seq, no need to call need_seqretry/done_seqretry. What do you think? Oleg. On 10/24, Oleg Nesterov wrote: > > Not for inclusion, just for discussion... > > Modulo naming, do you think the new need_seqretry_xxx() makes sense? > > Simpler to use and less error prone. thread_group_cputime() is changed > as an example. > --- > include/linux/seqlock.h | 10 ++++++++++ > kernel/sched/cputime.c | 9 +++------ > 2 files changed, 13 insertions(+), 6 deletions(-) > > diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h > index d0050c889f26..9b3bc4ce3332 100644 > --- a/include/linux/seqlock.h > +++ b/include/linux/seqlock.h > @@ -1165,6 +1165,16 @@ static inline int need_seqretry(seqlock_t *lock, int seq) > return !(seq & 1) && read_seqretry(lock, seq); > } > > +static inline int need_seqretry_xxx(seqlock_t *lock, int *seq) > +{ > + int ret = !(*seq & 1) && read_seqretry(lock, *seq); > + > + if (ret) > + ++*seq; /* make this counter odd */ > + > + return ret; > +} > + > /** > * done_seqretry() - end seqlock_t "locking or lockless" reader section > * @lock: Pointer to seqlock_t > diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c > index af7952f12e6c..45704a84baec 100644 > --- a/kernel/sched/cputime.c > +++ b/kernel/sched/cputime.c > @@ -314,7 +314,7 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times) > struct signal_struct *sig = tsk->signal; > u64 utime, stime; > struct task_struct *t; > - unsigned int seq, nextseq; > + unsigned int seq; > unsigned long flags; > > /* > @@ -330,9 +330,8 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times) > > rcu_read_lock(); > /* Attempt a lockless read on the first round. */ > - nextseq = 0; > + seq = 0; > do { > - seq = nextseq; > flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq); > times->utime = sig->utime; > times->stime = sig->stime; > @@ -344,9 +343,7 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times) > times->stime += stime; > times->sum_exec_runtime += read_sum_exec_runtime(t); > } > - /* If lockless access failed, take the lock. */ > - nextseq = 1; > - } while (need_seqretry(&sig->stats_lock, seq)); > + } while (need_seqretry_xxx(&sig->stats_lock, &seq)); > done_seqretry_irqrestore(&sig->stats_lock, seq, flags); > rcu_read_unlock(); > } > -- > 2.25.1.362.g51ebf55 > ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation 2023-10-24 12:08 [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation Oleg Nesterov 2023-10-24 12:08 ` [RFC PATCH 2/2] seqlock: introduce need_seqretry_xxx() Oleg Nesterov @ 2023-11-16 14:45 ` Oleg Nesterov 1 sibling, 0 replies; 4+ messages in thread From: Oleg Nesterov @ 2023-11-16 14:45 UTC (permalink / raw) To: Ingo Molnar, Peter Zijlstra Cc: Alexey Gladkov, Ahmed S. Darwish, Boqun Feng, Jonathan Corbet, Waiman Long, Will Deacon, linux-kernel, linux-doc Ping. Please ignore 2/2 for now (it obviously wasn't for inclusion), but the wrong documentation confuses the users. fs/afs, rxrpc_find_service_conn_rcu, nfsd_copy_write_verifier use read_seqbegin_or_lock/need_seqretry according to this doc and they are wrong. I am discussing the necessary changes in the code paths above, but can't we fix the documentation? On 10/24, Oleg Nesterov wrote: > > Half of the read_seqbegin_or_lock's users are buggy (I'll send the > fixes), and I guess this is because the documentation and the pseudo > code in Documentation/locking/seqlock.rst are wrong. > > Pseudo code: > > int seq = 0; > do { > read_seqbegin_or_lock(&foo_seqlock, &seq); > > /* ... [[read-side critical section]] ... */ > > } while (need_seqretry(&foo_seqlock, seq)); > > read_seqbegin_or_lock() returns with the even seq, need_seqretry() > doesn't change this counter. This means that seq is always even and > thus the locking pass is simply impossible. > > IOW, "_or_lock" has no effect and this code doesn't differ from > > do { > seq = read_seqbegin(&foo_seqlock); > > /* ... [[read-side critical section]] ... */ > > } while (read_seqretry(&foo_seqlock, seq)); > > Signed-off-by: Oleg Nesterov <oleg@redhat.com> > --- > Documentation/locking/seqlock.rst | 9 +++++---- > 1 file changed, 5 insertions(+), 4 deletions(-) > > diff --git a/Documentation/locking/seqlock.rst b/Documentation/locking/seqlock.rst > index bfda1a5fecad..4bdf8d4ed2a2 100644 > --- a/Documentation/locking/seqlock.rst > +++ b/Documentation/locking/seqlock.rst > @@ -218,13 +218,14 @@ Read path, three categories: > according to a passed marker. This is used to avoid lockless readers > starvation (too much retry loops) in case of a sharp spike in write > activity. First, a lockless read is tried (even marker passed). If > - that trial fails (odd sequence counter is returned, which is used as > - the next iteration marker), the lockless read is transformed to a > - full locking read and no retry loop is necessary:: > + that trial fails (sequence counter doesn't match), make the marker > + odd for the next iteration, the lockless read is transformed to a > + full locking read and no retry loop is necessary, for example:: > > /* marker; even initialization */ > - int seq = 0; > + int seq = 1; > do { > + seq++; /* 2 on the 1st/lockless path, otherwise odd */ > read_seqbegin_or_lock(&foo_seqlock, &seq); > > /* ... [[read-side critical section]] ... */ > -- > 2.25.1.362.g51ebf55 > ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2023-11-16 14:46 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-10-24 12:08 [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation Oleg Nesterov 2023-10-24 12:08 ` [RFC PATCH 2/2] seqlock: introduce need_seqretry_xxx() Oleg Nesterov 2023-10-24 13:24 ` Oleg Nesterov 2023-11-16 14:45 ` [PATCH 1/2] seqlock: fix the wrong read_seqbegin_or_lock/need_seqretry documentation Oleg Nesterov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).