diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-05-02 17:56:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-05-02 17:56:31 -0400 |
commit | 0279b3c0ada1d78882f24acf94ac4595bd657a89 (patch) | |
tree | ba31505ea6581b840604493d0233857bb7ce58d1 /kernel/sched | |
parent | 797994f81a8b2bdca2eecffa415c1e7a89a4f961 (diff) | |
parent | f3002134158092178be81339ec5a22ff80e6c308 (diff) |
Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull scheduler fixes from Ingo Molnar:
"This fixes the cputime scaling overflow problems for good without
having bad 32-bit overhead, and gets rid of the div64_u64_rem() helper
as well."
* 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
Revert "math64: New div64_u64_rem helper"
sched: Avoid prev->stime underflow
sched: Do not account bogus utime
sched: Avoid cputime scaling overflow
Diffstat (limited to 'kernel/sched')
-rw-r--r-- | kernel/sched/cputime.c | 80 |
1 files changed, 51 insertions, 29 deletions
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index ea32f02bf2c3..cc2dc3eea8a3 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c | |||
@@ -506,34 +506,47 @@ void account_idle_ticks(unsigned long ticks) | |||
506 | } | 506 | } |
507 | 507 | ||
508 | /* | 508 | /* |
509 | * Perform (stime * rtime) / total with reduced chances | 509 | * Perform (stime * rtime) / total, but avoid multiplication overflow by |
510 | * of multiplication overflows by using smaller factors | 510 | * loosing precision when the numbers are big. |
511 | * like quotient and remainders of divisions between | ||
512 | * rtime and total. | ||
513 | */ | 511 | */ |
514 | static cputime_t scale_stime(u64 stime, u64 rtime, u64 total) | 512 | static cputime_t scale_stime(u64 stime, u64 rtime, u64 total) |
515 | { | 513 | { |
516 | u64 rem, res, scaled; | 514 | u64 scaled; |
517 | 515 | ||
518 | if (rtime >= total) { | 516 | for (;;) { |
519 | /* | 517 | /* Make sure "rtime" is the bigger of stime/rtime */ |
520 | * Scale up to rtime / total then add | 518 | if (stime > rtime) { |
521 | * the remainder scaled to stime / total. | 519 | u64 tmp = rtime; rtime = stime; stime = tmp; |
522 | */ | 520 | } |
523 | res = div64_u64_rem(rtime, total, &rem); | 521 | |
524 | scaled = stime * res; | 522 | /* Make sure 'total' fits in 32 bits */ |
525 | scaled += div64_u64(stime * rem, total); | 523 | if (total >> 32) |
526 | } else { | 524 | goto drop_precision; |
527 | /* | 525 | |
528 | * Same in reverse: scale down to total / rtime | 526 | /* Does rtime (and thus stime) fit in 32 bits? */ |
529 | * then substract that result scaled to | 527 | if (!(rtime >> 32)) |
530 | * to the remaining part. | 528 | break; |
531 | */ | 529 | |
532 | res = div64_u64_rem(total, rtime, &rem); | 530 | /* Can we just balance rtime/stime rather than dropping bits? */ |
533 | scaled = div64_u64(stime, res); | 531 | if (stime >> 31) |
534 | scaled -= div64_u64(scaled * rem, total); | 532 | goto drop_precision; |
533 | |||
534 | /* We can grow stime and shrink rtime and try to make them both fit */ | ||
535 | stime <<= 1; | ||
536 | rtime >>= 1; | ||
537 | continue; | ||
538 | |||
539 | drop_precision: | ||
540 | /* We drop from rtime, it has more bits than stime */ | ||
541 | rtime >>= 1; | ||
542 | total >>= 1; | ||
535 | } | 543 | } |
536 | 544 | ||
545 | /* | ||
546 | * Make sure gcc understands that this is a 32x32->64 multiply, | ||
547 | * followed by a 64/32->64 divide. | ||
548 | */ | ||
549 | scaled = div_u64((u64) (u32) stime * (u64) (u32) rtime, (u32)total); | ||
537 | return (__force cputime_t) scaled; | 550 | return (__force cputime_t) scaled; |
538 | } | 551 | } |
539 | 552 | ||
@@ -545,7 +558,7 @@ static void cputime_adjust(struct task_cputime *curr, | |||
545 | struct cputime *prev, | 558 | struct cputime *prev, |
546 | cputime_t *ut, cputime_t *st) | 559 | cputime_t *ut, cputime_t *st) |
547 | { | 560 | { |
548 | cputime_t rtime, stime, total; | 561 | cputime_t rtime, stime, utime, total; |
549 | 562 | ||
550 | if (vtime_accounting_enabled()) { | 563 | if (vtime_accounting_enabled()) { |
551 | *ut = curr->utime; | 564 | *ut = curr->utime; |
@@ -568,13 +581,21 @@ static void cputime_adjust(struct task_cputime *curr, | |||
568 | */ | 581 | */ |
569 | rtime = nsecs_to_cputime(curr->sum_exec_runtime); | 582 | rtime = nsecs_to_cputime(curr->sum_exec_runtime); |
570 | 583 | ||
571 | if (!rtime) { | 584 | /* |
572 | stime = 0; | 585 | * Update userspace visible utime/stime values only if actual execution |
573 | } else if (!total) { | 586 | * time is bigger than already exported. Note that can happen, that we |
574 | stime = rtime; | 587 | * provided bigger values due to scaling inaccuracy on big numbers. |
575 | } else { | 588 | */ |
589 | if (prev->stime + prev->utime >= rtime) | ||
590 | goto out; | ||
591 | |||
592 | if (total) { | ||
576 | stime = scale_stime((__force u64)stime, | 593 | stime = scale_stime((__force u64)stime, |
577 | (__force u64)rtime, (__force u64)total); | 594 | (__force u64)rtime, (__force u64)total); |
595 | utime = rtime - stime; | ||
596 | } else { | ||
597 | stime = rtime; | ||
598 | utime = 0; | ||
578 | } | 599 | } |
579 | 600 | ||
580 | /* | 601 | /* |
@@ -583,8 +604,9 @@ static void cputime_adjust(struct task_cputime *curr, | |||
583 | * Let's enforce monotonicity. | 604 | * Let's enforce monotonicity. |
584 | */ | 605 | */ |
585 | prev->stime = max(prev->stime, stime); | 606 | prev->stime = max(prev->stime, stime); |
586 | prev->utime = max(prev->utime, rtime - prev->stime); | 607 | prev->utime = max(prev->utime, utime); |
587 | 608 | ||
609 | out: | ||
588 | *ut = prev->utime; | 610 | *ut = prev->utime; |
589 | *st = prev->stime; | 611 | *st = prev->stime; |
590 | } | 612 | } |