timer: Fix jiffies wrap behavior of round_jiffies*()
PATCH 1/1 timer: Fix jiffies wrap behavior of round_jiffies*()
Make sure that the round_jiffies*() functions return a time that is in the future when the jiffies counter is about to wrap.
--- kernel/timer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
kernel/timer.c +1 -3
@@ -149,9 +149,7 @@ static unsigned long round_jiffies_common(unsigned long j, int cpu,/* now that we have rounded, subtract the extra skew again */ j -= cpu *3 ; -if (j <= jiffies)/* rounding ate our timeout entirely; */ -return original; -return j; +return time_is_after_jiffies (j) ? j : original; }/**
Signature · 1 line
1.7.10.4
Quoted 2 lines · On Tue, 21 May 2013 20:43:50 +0200 Bart Van Assche <[email protected]>…
Make sure that the round_jiffies*() functions return a time that is in the future when the jiffies counter is about to wrap.
Actually "when the jiffies counter has recently wrapped".
I assume this was found by inspection?
kernel/timer.c static unsigned long round_jiffies_common(unsigned long j, int cpu, · line 151
Quoted 4 lines
kernel/timer.c +0
-1@@ -149,9 +149,7 @@ static unsigned long round_jiffies_common(unsigned long j, int cpu,/* now that we have rounded, subtract the extra skew again */ j -= cpu *3 ; -if (j <= jiffies)/* rounding ate our timeout entirely; */
This used to be a very common bug - we've weeded out many instances but I'm sure more still remain. We could perhaps have a checkpatch rule which looks for comparisons against jiffes (and any other time-measuring variables we can detect) and tells people to use time_after() and friends, or time_is_*_jiffies().
kernel/timer.c static unsigned long round_jiffies_common(unsigned long j, int cpu, · line 152
Quoted 4 lines
kernel/timer.c +1
-2-return original; -return j; +return time_is_after_jiffies (j) ? j : original; }
Your email client mangles patches, btw.
Quoted 9 lines · Andrew Morton
Quoted 2 lines · On Tue, 21 May 2013 20:43:50 +0200 Bart Van Assche <[email protected]>…
Make sure that the round_jiffies*() functions return a time that is in the future when the jiffies counter is about to wrap.
Actually "when the jiffies counter has recently wrapped".
I assume this was found by inspection?
Quoted 7 lines
kernel/timer.c static unsigned long round_jiffies_common(unsigned long j, int cpu, · line 151
Quoted 4 lines
kernel/timer.c +0
-1@@ -149,9 +149,7 @@ static unsigned long round_jiffies_common(unsigned long j, int cpu,/* now that we have rounded, subtract the extra skew again */ j -= cpu *3 ; -if (j <= jiffies)/* rounding ate our timeout entirely; */ This used to be a very common bug - we've weeded out many instances but I'm sure more still remain.
This list probably isn't comprehensive.
$ git grep -E "if\s*\((\s*jiffies\s*[\<\>]|.*[\<\>]=?\s*jiffies\b)"
drivers/acpi/acpi_pad.c: if (last_jiffies + round_robin_time * HZ < jiffies) {
drivers/acpi/acpi_pad.c: if (jiffies > expire_time) {
drivers/dma/ioat/dma_v2.c: if (jiffies > chan->timer.expires && timer_pending(&chan->timer)) {
drivers/infiniband/hw/ipath/ipath_sdma.c: if (jiffies < dd->ipath_sdma_abort_intr_timeout)
drivers/infiniband/hw/ipath/ipath_sdma.c: if (jiffies > dd->ipath_sdma_abort_jiffies) {
drivers/infiniband/ulp/ipoib/ipoib_cm.c: if (p && time_after_eq(jiffies, p->jiffies + IPOIB_CM_RX_UPDATE
drivers/infiniband/ulp/ipoib/ipoib_cm.c: if (time_before_eq(jiffies, p->jiffies + IPOIB_CM_RX_TIMEOUT))
drivers/input/misc/ati_remote2.c: if (!time_after_eq(jiffies, ar2->jiffies))
drivers/md/dm-log-userspace-base.c: else if (jiffies < limit)
drivers/misc/sgi-gru/grumain.c: if (gts->ts_steal_jiffies + GRU_STEAL_DELAY < jiffies)
drivers/net/ethernet/intel/e1000/e1000_ethtool.c: if (jiffies >= (time + 2)) {
drivers/net/ethernet/intel/e1000e/ethtool.c: if (jiffies >= (time + 20)) {
drivers/net/ethernet/micrel/ksz884x.c: if (next_jiffies < jiffies)
drivers/net/ethernet/micrel/ksz884x.c: } else if (jiffies >= hw_priv->counter[i].time) {
drivers/net/ethernet/micrel/ksz884x.c: if (hw_priv->pme_wait <= jiffies) {
drivers/net/ethernet/neterion/vxge/vxge-main.c: if (jiffies > fifo->jiffies + HZ / 100) {
drivers/net/ethernet/neterion/vxge/vxge-main.c: if (jiffies > ring->jiffies + HZ / 100) {
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c: if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) {
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c: if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) {
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c: if (jiffies > (QLCNIC_READD_AGE * HZ + time))
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c: if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime))
drivers/scsi/lpfc/lpfc_scsi.c: if ((phba->last_ramp_down_time + QUEUE_RAMP_DOWN_INTERVAL) > jiffies) {
drivers/staging/bcm/InterfaceIdleMode.c: if (timeout < jiffies )
drivers/staging/lustre/include/linux/libcfs/linux/linux-prim.h: if (jiffies > expire) { \
drivers/staging/speakup/speakup_acntpc.c: if (jiffies >= jiff_max && ch == SPACE) {
drivers/staging/speakup/speakup_decext.c: if (jiffies >= jiff_max) {
drivers/staging/speakup/speakup_decpc.c: if (jiffies >= jiff_max) {
drivers/staging/speakup/speakup_dectlk.c: if (jiffies >= jiff_max) {
kernel/timer.c: if (j <= jiffies) /* rounding ate our timeout entirely; */
net/rds/ib_send.c: if (ic->i_ack_queued + HZ/2 < jiffies)
net/rds/ib_send.c: if (send->s_queued + HZ/2 < jiffies)
net/rds/iw_send.c: if (ic->i_ack_queued + HZ/2 < jiffies)
net/rds/iw_send.c: if (send->s_queued + HZ/2 < jiffies)Quoted 3 lines
We could perhaps have a checkpatch rule
which looks for comparisons against jiffes (and any other time-measuring variables we can detect)
other variables like?
Quoted 2 lines
and tells people to use
time_after()and friends, or time_is_*_jiffies().
Quoted 5 lines · On Wed, 29 May 2013 16:17:47 -0700 Joe Perches <[email protected]> wrote:
Quoted 3 lines
We could perhaps have a checkpatch rule
which looks for comparisons against jiffes (and any other time-measuring variables we can detect)other variables like?
Grepping for time_after finds a bunch. There's no real pattern to it though.
Quoted 9 lines · Andrew Morton
Quoted 5 lines · On Wed, 29 May 2013 16:17:47 -0700 Joe Perches <[email protected]> wrote:
Quoted 3 lines
We could perhaps have a checkpatch rule
which looks for comparisons against jiffes (and any other time-measuring variables we can detect)other variables like?
Grepping for
time_afterfinds a bunch. There's no real pattern to it though.
Yup, that's the problem.
Thought I'd ask what I was missing though.
No variable really stands out as testable other
than jiffies.
I added this to my local queue and I'll send it later.
# check for comparisons of jiffies
if ($line =~ /\bif\s*\((\s*jiffies\s*[\<\>]|.*[\<\>]=?\s*jiffies\b)/) {
WARN("JIFFIES_COMPARISON",
"Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr);
}Quoted 9 lines · Andrew Morton
Quoted 5 lines · On Wed, 29 May 2013 16:17:47 -0700 Joe Perches <[email protected]> wrote:
Quoted 3 lines
We could perhaps have a checkpatch rule
which looks for comparisons against jiffes (and any other time-measuring variables we can detect)other variables like?
Grepping for
time_afterfinds a bunch. There's no real pattern to it though.
get_jiffies_64() should probably be added as
another test too.
Also, these might be wrong:
arch/arm/kernel/smp_twd.c: while (get_jiffies_64() < waitjiffies)
arch/arm/kernel/smp_twd.c: while (get_jiffies_64() < waitjiffies)
fs/fuse/dir.c: else if (fuse_dentry_time(entry) < get_jiffies_64()) {
fs/fuse/dir.c: if (fi->i_time < get_jiffies_64()) {
fs/fuse/dir.c: if (fi->i_time < get_jiffies_64()) {Quoted 14 lines · On Wed, 29 May 2013 17:13:49 -0700 Joe Perches <[email protected]> wrote:
Quoted 9 lines · Andrew Morton
Quoted 5 lines · On Wed, 29 May 2013 16:17:47 -0700 Joe Perches <[email protected]> wrote:
Quoted 3 lines
We could perhaps have a checkpatch rule
which looks for comparisons against jiffes (and any other time-measuring variables we can detect)other variables like?
Grepping for
time_afterfinds a bunch. There's no real pattern to it though.
get_jiffies_64()should probably be added as
another test too.
Quoted 8 lines
Also, these might be wrong:
arch/arm/kernel/smp_twd.c: while (get_jiffies_64() < waitjiffies) arch/arm/kernel/smp_twd.c: while (get_jiffies_64() < waitjiffies) fs/fuse/dir.c: else if (fuse_dentry_time(entry) < get_jiffies_64()) { fs/fuse/dir.c: if (fi->i_time < get_jiffies_64()) { fs/fuse/dir.c: if (fi->i_time < get_jiffies_64()) {
Yup. Normally a 64-bit jiffy will wrap around shortly after the heat death of the universe, but
a) it's derived from jiffies, which we evilly cause to wrap after 5 minutes uptime and
b) it's derived from jiffies, which is 32-bit on 32-bit and hence wraps every 49 days (HZ=1000).
Quoted 8 lines · Andrew Morton
Quoted 2 lines · On Tue, 21 May 2013 20:43:50 +0200 Bart Van Assche <[email protected]>…
Make sure that the round_jiffies*() functions return a time that is in the future when the jiffies counter is about to wrap.
Actually "when the jiffies counter has recently wrapped".
I assume this was found by inspection?
Hello Andrew,
You are correct, this was found via source code inspection. I started reviewing the round_jiffies*() implementation because I was chasing a bug in a kernel driver using one of these functions.
Quoted 6 lines
kernel/timer.c static unsigned long round_jiffies_common(unsigned long j, int cpu, · line 152
Quoted 4 lines
kernel/timer.c +1
-2-return original; -return j; +return time_is_after_jiffies (j) ? j : original; }Your email client mangles patches, btw.
Sorry. Will take more care in the future.
Bart.
tip-bot for Bart Van Assche
merge bot[tip:timers/core] timer: Fix jiffies wrap behavior of round_jiffies_common()
In reply to Bart Van Asschetimer: Fix jiffies wrap behavior of round_jiffies_common()
Commit-ID: 9e04d3804d3ac97d8c03a41d78d0f0674b5d01e1 Gitweb: http://git.kernel.org/tip/9e04d3804d3ac97d8c03a41d78d0f0674b5d01e1 Author: Bart Van Assche <[email protected]> AuthorDate: Tue, 21 May 2013 20:43:50 +0200 Committer: Thomas Gleixner <[email protected]> CommitDate: Fri, 28 Jun 2013 17:10:11 +0200
timer: Fix jiffies wrap behavior of round_jiffies_common()
Direct compare of jiffies related values does not work in the wrap around case. Replace it with time_is_after_jiffies().
--- kernel/timer.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
kernel/timer.c +5 -3
@@ -149,9 +149,11 @@ static unsigned long round_jiffies_common(unsigned long j, int cpu,/* now that we have rounded, subtract the extra skew again */ j -= cpu *3 ; -if (j <= jiffies)/* rounding ate our timeout entirely; */ -return original; -return j; +/* +* Make sure j is still in the future. Otherwise return the +* unmodified value. +*/ +return time_is_after_jiffies (j) ? j : original; }/**