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

avoid overflows in kernel/time.c

When the conversion factor between jiffies and milli- or microseconds is
not a single multiply or divide, as for the case of HZ == 300, we currently
do a multiply followed by a divide. The intervening result, however, is
subject to overflows, especially since the fraction is not simplified (for
HZ == 300, we multiply by 300 and divide by 1000).

This is exposed to the user when passing a large timeout to poll(), for
example.

This patch replaces the multiply-divide with a reciprocal multiplication on
32-bit platforms. When the input is an unsigned long, there is no portable
way to do this on 64-bit platforms there is no portable way to do this
since it requires a 128-bit intermediate result (which gcc does support on
64-bit platforms but may generate libgcc calls, e.g. on 64-bit s390), but
since the output is a 32-bit integer in the cases affected, just simplify
the multiply-divide (*3/10 instead of *300/1000).

The reciprocal multiply used can have off-by-one errors in the upper half
of the valid output range. This could be avoided at the expense of having
to deal with a potential 65-bit intermediate result. Since the intent is
to avoid overflow problems and most of the other time conversions are only
semiexact, the off-by-one errors were considered an acceptable tradeoff.

At Ralf Baechle's suggestion, this version uses a Perl script to compute
the necessary constants. We already have dependencies on Perl for kernel
compiles. This does, however, require the Perl module Math::BigInt, which
is included in the standard Perl distribution starting with version 5.8.0.
In order to support older versions of Perl, include a table of canned
constants in the script itself, and structure the script so that
Math::BigInt isn't required if pulling values from said table.

Running the script requires that the HZ value is available from the
Makefile. Thus, this patch also adds the Kconfig variable CONFIG_HZ to the
architectures which didn't already have it (alpha, cris, frv, h8300, m32r,
m68k, m68knommu, sparc, v850, and xtensa.) It does *not* touch the sh or
sh64 architectures, since Paul Mundt has dealt with those separately in the
sh tree.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Cc: Ralf Baechle <ralf@linux-mips.org>,
Cc: Sam Ravnborg <sam@ravnborg.org>,
Cc: Paul Mundt <lethal@linux-sh.org>,
Cc: Richard Henderson <rth@twiddle.net>,
Cc: Michael Starvik <starvik@axis.com>,
Cc: David Howells <dhowells@redhat.com>,
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>,
Cc: Hirokazu Takata <takata@linux-m32r.org>,
Cc: Geert Uytterhoeven <geert@linux-m68k.org>,
Cc: Roman Zippel <zippel@linux-m68k.org>,
Cc: William L. Irwin <sparclinux@vger.kernel.org>,
Cc: Chris Zankel <chris@zankel.net>,
Cc: H. Peter Anvin <hpa@zytor.com>,
Cc: Jan Engelhardt <jengelh@computergmbh.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

H. Peter Anvin and committed by
Linus Torvalds
bdc80787 7ef3d2fd

+486 -68
+5
arch/alpha/Kconfig
··· 615 615 616 616 Take the default (1) unless you want more control or more info. 617 617 618 + config HZ 619 + int 620 + default 1200 if ALPHA_RAWHIDE 621 + default 1024 622 + 618 623 source "drivers/pci/Kconfig" 619 624 source "drivers/eisa/Kconfig" 620 625
+4
arch/cris/Kconfig
··· 55 55 bool 56 56 default y 57 57 58 + config HZ 59 + int 60 + default 100 61 + 58 62 source "init/Kconfig" 59 63 60 64 menu "General setup"
+4
arch/frv/Kconfig
··· 57 57 bool 58 58 default y 59 59 60 + config HZ 61 + int 62 + default 1000 63 + 60 64 mainmenu "Fujitsu FR-V Kernel Configuration" 61 65 62 66 source "init/Kconfig"
+4
arch/h8300/Kconfig
··· 82 82 bool 83 83 default n 84 84 85 + config HZ 86 + int 87 + default 100 88 + 85 89 source "init/Kconfig" 86 90 87 91 source "arch/h8300/Kconfig.cpu"
+4
arch/m32r/Kconfig
··· 38 38 config ARCH_SUPPORTS_AOUT 39 39 def_bool y 40 40 41 + config HZ 42 + int 43 + default 100 44 + 41 45 source "init/Kconfig" 42 46 43 47
+4
arch/m68k/Kconfig
··· 55 55 config ARCH_SUPPORTS_AOUT 56 56 def_bool y 57 57 58 + config HZ 59 + int 60 + default 100 61 + 58 62 mainmenu "Linux/68k Kernel Configuration" 59 63 60 64 source "init/Kconfig"
+5
arch/m68knommu/Kconfig
··· 525 525 running more threads on a system and also reduces the pressure 526 526 on the VM subsystem for higher order allocations. 527 527 528 + config HZ 529 + int 530 + default 1000 if CLEOPATRA 531 + default 100 532 + 528 533 comment "RAM configuration" 529 534 530 535 config RAMBASE
+4
arch/sparc/Kconfig
··· 30 30 config ARCH_SUPPORTS_AOUT 31 31 def_bool y 32 32 33 + config HZ 34 + int 35 + default 100 36 + 33 37 source "init/Kconfig" 34 38 35 39 menu "General machine setup"
+7
arch/v850/Kconfig
··· 215 215 bool 216 216 default !V850E_CACHE && !V850E2_CACHE 217 217 218 + # HZ depends on the platform 219 + config HZ 220 + int 221 + default 24 if V850E_SIM || V850E2_SIM85E2 222 + default 122 if V850E2_FPGA85E2C 223 + default 100 224 + 218 225 #### Misc config 219 226 220 227 config ROM_KERNEL
+4
arch/xtensa/Kconfig
··· 49 49 config NO_IOPORT 50 50 def_bool y 51 51 52 + config HZ 53 + int 54 + default 100 55 + 52 56 source "init/Kconfig" 53 57 54 58 menu "Processor type and features"
+1 -9
include/asm-alpha/param.h
··· 5 5 hardware ignores reprogramming. We also need userland buy-in to the 6 6 change in HZ, since this is visible in the wait4 resources etc. */ 7 7 8 - 9 - #ifndef HZ 10 - # ifndef CONFIG_ALPHA_RAWHIDE 11 - # define HZ 1024 12 - # else 13 - # define HZ 1200 14 - # endif 15 - #endif 16 - 8 + #define HZ CONFIG_HZ 17 9 #define USER_HZ HZ 18 10 19 11 #define EXEC_PAGESIZE 8192
+1 -1
include/asm-cris/param.h
··· 3 3 4 4 /* Currently we assume that HZ=100 is good for CRIS. */ 5 5 #ifdef __KERNEL__ 6 - # define HZ 100 /* Internal kernel timer frequency */ 6 + # define HZ CONFIG_HZ /* Internal kernel timer frequency */ 7 7 # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ 8 8 # define CLOCKS_PER_SEC (USER_HZ) /* like times() */ 9 9 #endif
+1 -1
include/asm-frv/param.h
··· 2 2 #define _ASM_PARAM_H 3 3 4 4 #ifdef __KERNEL__ 5 - #define HZ 1000 /* Internal kernel timer frequency */ 5 + #define HZ CONFIG_HZ /* Internal kernel timer frequency */ 6 6 #define USER_HZ 100 /* .. some user interfaces are in "ticks" */ 7 7 #define CLOCKS_PER_SEC (USER_HZ) /* like times() */ 8 8 #endif
+1 -1
include/asm-h8300/param.h
··· 3 3 4 4 5 5 #ifndef HZ 6 - #define HZ 100 6 + #define HZ CONFIG_HZ 7 7 #endif 8 8 9 9 #ifdef __KERNEL__
+1 -1
include/asm-m32r/param.h
··· 2 2 #define _ASM_M32R_PARAM_H 3 3 4 4 #ifdef __KERNEL__ 5 - # define HZ 100 /* Internal kernel timer frequency */ 5 + # define HZ CONFIG_HZ /* Internal kernel timer frequency */ 6 6 # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ 7 7 # define CLOCKS_PER_SEC (USER_HZ) /* like times() */ 8 8 #endif
+1 -1
include/asm-m68k/param.h
··· 2 2 #define _M68K_PARAM_H 3 3 4 4 #ifdef __KERNEL__ 5 - # define HZ 100 /* Internal kernel timer frequency */ 5 + # define HZ CONFIG_HZ /* Internal kernel timer frequency */ 6 6 # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ 7 7 # define CLOCKS_PER_SEC (USER_HZ) /* like times() */ 8 8 #endif
+1 -7
include/asm-m68knommu/param.h
··· 1 1 #ifndef _M68KNOMMU_PARAM_H 2 2 #define _M68KNOMMU_PARAM_H 3 3 4 - 5 - #if defined(CONFIG_CLEOPATRA) 6 - #define HZ 1000 7 - #endif 8 - #ifndef HZ 9 - #define HZ 100 10 - #endif 4 + #define HZ CONFIG_HZ 11 5 12 6 #ifdef __KERNEL__ 13 7 #define USER_HZ HZ
+1 -1
include/asm-sparc/param.h
··· 3 3 #define _ASMSPARC_PARAM_H 4 4 5 5 #ifdef __KERNEL__ 6 - # define HZ 100 /* Internal kernel timer frequency */ 6 + # define HZ CONFIG_HZ /* Internal kernel timer frequency */ 7 7 # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ 8 8 # define CLOCKS_PER_SEC (USER_HZ) 9 9 #endif
-6
include/asm-v850/anna.h
··· 134 134 #define V850E_TIMER_D_TMCD_CS_MIN 1 /* min 2^1 divider */ 135 135 136 136 137 - /* For <asm/param.h> */ 138 - #ifndef HZ 139 - #define HZ 100 140 - #endif 141 - 142 - 143 137 #endif /* __V850_ANNA_H__ */
-6
include/asm-v850/as85ep1.h
··· 149 149 #define V850E_TIMER_D_TMCD_CS_MIN 2 /* min 2^2 divider */ 150 150 151 151 152 - /* For <asm/param.h> */ 153 - #ifndef HZ 154 - #define HZ 100 155 - #endif 156 - 157 - 158 152 #endif /* __V850_AS85EP1_H__ */
-6
include/asm-v850/fpga85e2c.h
··· 79 79 #endif 80 80 81 81 82 - /* For <asm/param.h> */ 83 - #ifndef HZ 84 - #define HZ 122 /* actually, 8.192ms ticks =~ 122.07 */ 85 - #endif 86 - 87 - 88 82 #endif /* __V850_FPGA85E2C_H__ */
+1 -2
include/asm-v850/param.h
··· 23 23 #define MAXHOSTNAMELEN 64 /* max length of hostname */ 24 24 25 25 #ifdef __KERNEL__ 26 - #include <asm/machdep.h> /* For HZ */ 27 - 26 + # define HZ CONFIG_HZ 28 27 # define USER_HZ 100 29 28 # define CLOCKS_PER_SEC USER_HZ 30 29 #endif
-6
include/asm-v850/rte_cb.h
··· 69 69 #endif /* CONFIG_RTE_MB_A_PCI */ 70 70 71 71 72 - /* For <asm/param.h> */ 73 - #ifndef HZ 74 - #define HZ 100 75 - #endif 76 - 77 - 78 72 #ifndef __ASSEMBLY__ 79 73 extern void rte_cb_early_init (void); 80 74 extern void rte_cb_init_irqs (void);
-5
include/asm-v850/sim.h
··· 40 40 #define R0_RAM_ADDR 0xFFFFF000 41 41 42 42 43 - /* For <asm/param.h> */ 44 - #ifndef HZ 45 - #define HZ 24 /* Minimum supported frequency. */ 46 - #endif 47 - 48 43 /* For <asm/irq.h> */ 49 44 #define NUM_CPU_IRQS 6 50 45
-6
include/asm-v850/sim85e2.h
··· 66 66 #define R0_RAM_ADDR 0xFFFFE000 67 67 68 68 69 - /* For <asm/param.h> */ 70 - #ifndef HZ 71 - #define HZ 24 /* Minimum supported frequency. */ 72 - #endif 73 - 74 - 75 69 #endif /* __V850_SIM85E2_H__ */
+1 -1
include/asm-xtensa/param.h
··· 12 12 #define _XTENSA_PARAM_H 13 13 14 14 #ifdef __KERNEL__ 15 - # define HZ 100 /* internal timer frequency */ 15 + # define HZ CONFIG_HZ /* internal timer frequency */ 16 16 # define USER_HZ 100 /* for user interfaces in "ticks" */ 17 17 # define CLOCKS_PER_SEC (USER_HZ) /* frequnzy at which times() counts */ 18 18 #endif
+8
kernel/Makefile
··· 91 91 targets += config_data.h 92 92 $(obj)/config_data.h: $(obj)/config_data.gz FORCE 93 93 $(call if_changed,ikconfiggz) 94 + 95 + $(obj)/time.o: $(obj)/timeconst.h 96 + 97 + quiet_cmd_timeconst = TIMEC $@ 98 + cmd_timeconst = $(PERL) $< $(CONFIG_HZ) > $@ 99 + targets += timeconst.h 100 + $(obj)/timeconst.h: $(src)/timeconst.pl FORCE 101 + $(call if_changed,timeconst)
+21 -8
kernel/time.c
··· 39 39 #include <asm/uaccess.h> 40 40 #include <asm/unistd.h> 41 41 42 + #include "timeconst.h" 43 + 42 44 /* 43 45 * The timezone where the local system is located. Used as a default by some 44 46 * programs who obtain this value by using gettimeofday. ··· 95 93 96 94 #endif /* __ARCH_WANT_SYS_TIME */ 97 95 98 - asmlinkage long sys_gettimeofday(struct timeval __user *tv, struct timezone __user *tz) 96 + asmlinkage long sys_gettimeofday(struct timeval __user *tv, 97 + struct timezone __user *tz) 99 98 { 100 99 if (likely(tv != NULL)) { 101 100 struct timeval ktv; ··· 121 118 * hard to make the program warp the clock precisely n hours) or 122 119 * compile in the timezone information into the kernel. Bad, bad.... 123 120 * 124 - * - TYT, 1992-01-01 121 + * - TYT, 1992-01-01 125 122 * 126 123 * The best thing to do is to keep the CMOS clock in universal time (UTC) 127 124 * as real UNIX machines always do it. This avoids all headaches about ··· 243 240 #elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) 244 241 return (j + (HZ / MSEC_PER_SEC) - 1)/(HZ / MSEC_PER_SEC); 245 242 #else 246 - return (j * MSEC_PER_SEC) / HZ; 243 + # if BITS_PER_LONG == 32 244 + return ((u64)HZ_TO_MSEC_MUL32 * j) >> HZ_TO_MSEC_SHR32; 245 + # else 246 + return (j * HZ_TO_MSEC_NUM) / HZ_TO_MSEC_DEN; 247 + # endif 247 248 #endif 248 249 } 249 250 EXPORT_SYMBOL(jiffies_to_msecs); ··· 259 252 #elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC) 260 253 return (j + (HZ / USEC_PER_SEC) - 1)/(HZ / USEC_PER_SEC); 261 254 #else 262 - return (j * USEC_PER_SEC) / HZ; 255 + # if BITS_PER_LONG == 32 256 + return ((u64)HZ_TO_USEC_MUL32 * j) >> HZ_TO_USEC_SHR32; 257 + # else 258 + return (j * HZ_TO_USEC_NUM) / HZ_TO_USEC_DEN; 259 + # endif 263 260 #endif 264 261 } 265 262 EXPORT_SYMBOL(jiffies_to_usecs); ··· 363 352 * normalize to the timespec storage format 364 353 * 365 354 * Note: The tv_nsec part is always in the range of 366 - * 0 <= tv_nsec < NSEC_PER_SEC 355 + * 0 <= tv_nsec < NSEC_PER_SEC 367 356 * For negative values only the tv_sec field is negative ! 368 357 */ 369 358 void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec) ··· 464 453 /* 465 454 * Generic case - multiply, round and divide. But first 466 455 * check that if we are doing a net multiplication, that 467 - * we wouldnt overflow: 456 + * we wouldn't overflow: 468 457 */ 469 458 if (HZ > MSEC_PER_SEC && m > jiffies_to_msecs(MAX_JIFFY_OFFSET)) 470 459 return MAX_JIFFY_OFFSET; 471 460 472 - return (m * HZ + MSEC_PER_SEC - 1) / MSEC_PER_SEC; 461 + return ((u64)MSEC_TO_HZ_MUL32 * m + MSEC_TO_HZ_ADJ32) 462 + >> MSEC_TO_HZ_SHR32; 473 463 #endif 474 464 } 475 465 EXPORT_SYMBOL(msecs_to_jiffies); ··· 484 472 #elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC) 485 473 return u * (HZ / USEC_PER_SEC); 486 474 #else 487 - return (u * HZ + USEC_PER_SEC - 1) / USEC_PER_SEC; 475 + return ((u64)USEC_TO_HZ_MUL32 * u + USEC_TO_HZ_ADJ32) 476 + >> USEC_TO_HZ_SHR32; 488 477 #endif 489 478 } 490 479 EXPORT_SYMBOL(usecs_to_jiffies);
+402
kernel/timeconst.pl
··· 1 + #!/usr/bin/perl 2 + # ----------------------------------------------------------------------- 3 + # 4 + # Copyright 2007 rPath, Inc. - All Rights Reserved 5 + # 6 + # This file is part of the Linux kernel, and is made available under 7 + # the terms of the GNU General Public License version 2 or (at your 8 + # option) any later version; incorporated herein by reference. 9 + # 10 + # ----------------------------------------------------------------------- 11 + # 12 + 13 + # 14 + # Usage: timeconst.pl HZ > timeconst.h 15 + # 16 + 17 + # Precomputed values for systems without Math::BigInt 18 + # Generated by: 19 + # timeconst.pl --can 24 32 48 64 100 122 128 200 250 256 300 512 1000 1024 1200 20 + %canned_values = ( 21 + 24 => [ 22 + '0xa6aaaaab','0x2aaaaaa',26, 23 + '0xa6aaaaaaaaaaaaab','0x2aaaaaaaaaaaaaa',58, 24 + 125,3, 25 + '0xc49ba5e4','0x1fbe76c8b4',37, 26 + '0xc49ba5e353f7ceda','0x1fbe76c8b439581062',69, 27 + 3,125, 28 + '0xa2c2aaab','0xaaaa',16, 29 + '0xa2c2aaaaaaaaaaab','0xaaaaaaaaaaaa',48, 30 + 125000,3, 31 + '0xc9539b89','0x7fffbce4217d',47, 32 + '0xc9539b8887229e91','0x7fffbce4217d2849cb25',79, 33 + 3,125000, 34 + ], 32 => [ 35 + '0xfa000000','0x6000000',27, 36 + '0xfa00000000000000','0x600000000000000',59, 37 + 125,4, 38 + '0x83126e98','0xfdf3b645a',36, 39 + '0x83126e978d4fdf3c','0xfdf3b645a1cac0831',68, 40 + 4,125, 41 + '0xf4240000','0x0',17, 42 + '0xf424000000000000','0x0',49, 43 + 31250,1, 44 + '0x8637bd06','0x3fff79c842fa',46, 45 + '0x8637bd05af6c69b6','0x3fff79c842fa5093964a',78, 46 + 1,31250, 47 + ], 48 => [ 48 + '0xa6aaaaab','0x6aaaaaa',27, 49 + '0xa6aaaaaaaaaaaaab','0x6aaaaaaaaaaaaaa',59, 50 + 125,6, 51 + '0xc49ba5e4','0xfdf3b645a',36, 52 + '0xc49ba5e353f7ceda','0xfdf3b645a1cac0831',68, 53 + 6,125, 54 + '0xa2c2aaab','0x15555',17, 55 + '0xa2c2aaaaaaaaaaab','0x1555555555555',49, 56 + 62500,3, 57 + '0xc9539b89','0x3fffbce4217d',46, 58 + '0xc9539b8887229e91','0x3fffbce4217d2849cb25',78, 59 + 3,62500, 60 + ], 64 => [ 61 + '0xfa000000','0xe000000',28, 62 + '0xfa00000000000000','0xe00000000000000',60, 63 + 125,8, 64 + '0x83126e98','0x7ef9db22d',35, 65 + '0x83126e978d4fdf3c','0x7ef9db22d0e560418',67, 66 + 8,125, 67 + '0xf4240000','0x0',18, 68 + '0xf424000000000000','0x0',50, 69 + 15625,1, 70 + '0x8637bd06','0x1fff79c842fa',45, 71 + '0x8637bd05af6c69b6','0x1fff79c842fa5093964a',77, 72 + 1,15625, 73 + ], 100 => [ 74 + '0xa0000000','0x0',28, 75 + '0xa000000000000000','0x0',60, 76 + 10,1, 77 + '0xcccccccd','0x733333333',35, 78 + '0xcccccccccccccccd','0x73333333333333333',67, 79 + 1,10, 80 + '0x9c400000','0x0',18, 81 + '0x9c40000000000000','0x0',50, 82 + 10000,1, 83 + '0xd1b71759','0x1fff2e48e8a7',45, 84 + '0xd1b71758e219652c','0x1fff2e48e8a71de69ad4',77, 85 + 1,10000, 86 + ], 122 => [ 87 + '0x8325c53f','0xfbcda3a',28, 88 + '0x8325c53ef368eb05','0xfbcda3ac10c9714',60, 89 + 500,61, 90 + '0xf9db22d1','0x7fbe76c8b',35, 91 + '0xf9db22d0e560418a','0x7fbe76c8b43958106',67, 92 + 61,500, 93 + '0x8012e2a0','0x3ef36',18, 94 + '0x8012e29f79b47583','0x3ef368eb04325',50, 95 + 500000,61, 96 + '0xffda4053','0x1ffffbce4217',45, 97 + '0xffda4052d666a983','0x1ffffbce4217d2849cb2',77, 98 + 61,500000, 99 + ], 128 => [ 100 + '0xfa000000','0x1e000000',29, 101 + '0xfa00000000000000','0x1e00000000000000',61, 102 + 125,16, 103 + '0x83126e98','0x3f7ced916',34, 104 + '0x83126e978d4fdf3c','0x3f7ced916872b020c',66, 105 + 16,125, 106 + '0xf4240000','0x40000',19, 107 + '0xf424000000000000','0x4000000000000',51, 108 + 15625,2, 109 + '0x8637bd06','0xfffbce4217d',44, 110 + '0x8637bd05af6c69b6','0xfffbce4217d2849cb25',76, 111 + 2,15625, 112 + ], 200 => [ 113 + '0xa0000000','0x0',29, 114 + '0xa000000000000000','0x0',61, 115 + 5,1, 116 + '0xcccccccd','0x333333333',34, 117 + '0xcccccccccccccccd','0x33333333333333333',66, 118 + 1,5, 119 + '0x9c400000','0x0',19, 120 + '0x9c40000000000000','0x0',51, 121 + 5000,1, 122 + '0xd1b71759','0xfff2e48e8a7',44, 123 + '0xd1b71758e219652c','0xfff2e48e8a71de69ad4',76, 124 + 1,5000, 125 + ], 250 => [ 126 + '0x80000000','0x0',29, 127 + '0x8000000000000000','0x0',61, 128 + 4,1, 129 + '0x80000000','0x180000000',33, 130 + '0x8000000000000000','0x18000000000000000',65, 131 + 1,4, 132 + '0xfa000000','0x0',20, 133 + '0xfa00000000000000','0x0',52, 134 + 4000,1, 135 + '0x83126e98','0x7ff7ced9168',43, 136 + '0x83126e978d4fdf3c','0x7ff7ced916872b020c4',75, 137 + 1,4000, 138 + ], 256 => [ 139 + '0xfa000000','0x3e000000',30, 140 + '0xfa00000000000000','0x3e00000000000000',62, 141 + 125,32, 142 + '0x83126e98','0x1fbe76c8b',33, 143 + '0x83126e978d4fdf3c','0x1fbe76c8b43958106',65, 144 + 32,125, 145 + '0xf4240000','0xc0000',20, 146 + '0xf424000000000000','0xc000000000000',52, 147 + 15625,4, 148 + '0x8637bd06','0x7ffde7210be',43, 149 + '0x8637bd05af6c69b6','0x7ffde7210be9424e592',75, 150 + 4,15625, 151 + ], 300 => [ 152 + '0xd5555556','0x2aaaaaaa',30, 153 + '0xd555555555555556','0x2aaaaaaaaaaaaaaa',62, 154 + 10,3, 155 + '0x9999999a','0x1cccccccc',33, 156 + '0x999999999999999a','0x1cccccccccccccccc',65, 157 + 3,10, 158 + '0xd0555556','0xaaaaa',20, 159 + '0xd055555555555556','0xaaaaaaaaaaaaa',52, 160 + 10000,3, 161 + '0x9d495183','0x7ffcb923a29',43, 162 + '0x9d495182a9930be1','0x7ffcb923a29c779a6b5',75, 163 + 3,10000, 164 + ], 512 => [ 165 + '0xfa000000','0x7e000000',31, 166 + '0xfa00000000000000','0x7e00000000000000',63, 167 + 125,64, 168 + '0x83126e98','0xfdf3b645',32, 169 + '0x83126e978d4fdf3c','0xfdf3b645a1cac083',64, 170 + 64,125, 171 + '0xf4240000','0x1c0000',21, 172 + '0xf424000000000000','0x1c000000000000',53, 173 + 15625,8, 174 + '0x8637bd06','0x3ffef39085f',42, 175 + '0x8637bd05af6c69b6','0x3ffef39085f4a1272c9',74, 176 + 8,15625, 177 + ], 1000 => [ 178 + '0x80000000','0x0',31, 179 + '0x8000000000000000','0x0',63, 180 + 1,1, 181 + '0x80000000','0x0',31, 182 + '0x8000000000000000','0x0',63, 183 + 1,1, 184 + '0xfa000000','0x0',22, 185 + '0xfa00000000000000','0x0',54, 186 + 1000,1, 187 + '0x83126e98','0x1ff7ced9168',41, 188 + '0x83126e978d4fdf3c','0x1ff7ced916872b020c4',73, 189 + 1,1000, 190 + ], 1024 => [ 191 + '0xfa000000','0xfe000000',32, 192 + '0xfa00000000000000','0xfe00000000000000',64, 193 + 125,128, 194 + '0x83126e98','0x7ef9db22',31, 195 + '0x83126e978d4fdf3c','0x7ef9db22d0e56041',63, 196 + 128,125, 197 + '0xf4240000','0x3c0000',22, 198 + '0xf424000000000000','0x3c000000000000',54, 199 + 15625,16, 200 + '0x8637bd06','0x1fff79c842f',41, 201 + '0x8637bd05af6c69b6','0x1fff79c842fa5093964',73, 202 + 16,15625, 203 + ], 1200 => [ 204 + '0xd5555556','0xd5555555',32, 205 + '0xd555555555555556','0xd555555555555555',64, 206 + 5,6, 207 + '0x9999999a','0x66666666',31, 208 + '0x999999999999999a','0x6666666666666666',63, 209 + 6,5, 210 + '0xd0555556','0x2aaaaa',22, 211 + '0xd055555555555556','0x2aaaaaaaaaaaaa',54, 212 + 2500,3, 213 + '0x9d495183','0x1ffcb923a29',41, 214 + '0x9d495182a9930be1','0x1ffcb923a29c779a6b5',73, 215 + 3,2500, 216 + ] 217 + ); 218 + 219 + $has_bigint = eval 'use Math::BigInt qw(bgcd); 1;'; 220 + 221 + sub bint($) 222 + { 223 + my($x) = @_; 224 + return Math::BigInt->new($x); 225 + } 226 + 227 + # 228 + # Constants for division by reciprocal multiplication. 229 + # (bits, numerator, denominator) 230 + # 231 + sub fmul($$$) 232 + { 233 + my ($b,$n,$d) = @_; 234 + 235 + $n = bint($n); 236 + $d = bint($d); 237 + 238 + return scalar (($n << $b)+$d-bint(1))/$d; 239 + } 240 + 241 + sub fadj($$$) 242 + { 243 + my($b,$n,$d) = @_; 244 + 245 + $n = bint($n); 246 + $d = bint($d); 247 + 248 + $d = $d/bgcd($n, $d); 249 + return scalar (($d-bint(1)) << $b)/$d; 250 + } 251 + 252 + sub fmuls($$$) { 253 + my($b,$n,$d) = @_; 254 + my($s,$m); 255 + my($thres) = bint(1) << ($b-1); 256 + 257 + $n = bint($n); 258 + $d = bint($d); 259 + 260 + for ($s = 0; 1; $s++) { 261 + $m = fmul($s,$n,$d); 262 + return $s if ($m >= $thres); 263 + } 264 + return 0; 265 + } 266 + 267 + # Provides mul, adj, and shr factors for a specific 268 + # (bit, time, hz) combination 269 + sub muladj($$$) { 270 + my($b, $t, $hz) = @_; 271 + my $s = fmuls($b, $t, $hz); 272 + my $m = fmul($s, $t, $hz); 273 + my $a = fadj($s, $t, $hz); 274 + return ($m->as_hex(), $a->as_hex(), $s); 275 + } 276 + 277 + # Provides numerator, denominator values 278 + sub numden($$) { 279 + my($n, $d) = @_; 280 + my $g = bgcd($n, $d); 281 + return ($n/$g, $d/$g); 282 + } 283 + 284 + # All values for a specific (time, hz) combo 285 + sub conversions($$) { 286 + my ($t, $hz) = @_; 287 + my @val = (); 288 + 289 + # HZ_TO_xx 290 + push(@val, muladj(32, $t, $hz)); 291 + push(@val, muladj(64, $t, $hz)); 292 + push(@val, numden($t, $hz)); 293 + 294 + # xx_TO_HZ 295 + push(@val, muladj(32, $hz, $t)); 296 + push(@val, muladj(64, $hz, $t)); 297 + push(@val, numden($hz, $t)); 298 + 299 + return @val; 300 + } 301 + 302 + sub compute_values($) { 303 + my($hz) = @_; 304 + my @val = (); 305 + my $s, $m, $a, $g; 306 + 307 + if (!$has_bigint) { 308 + die "$0: HZ == $hz not canned and ". 309 + "Math::BigInt not available\n"; 310 + } 311 + 312 + # MSEC conversions 313 + push(@val, conversions(1000, $hz)); 314 + 315 + # USEC conversions 316 + push(@val, conversions(1000000, $hz)); 317 + 318 + return @val; 319 + } 320 + 321 + sub output($@) 322 + { 323 + my($hz, @val) = @_; 324 + my $pfx, $bit, $suf, $s, $m, $a; 325 + 326 + print "/* Automatically generated by kernel/timeconst.pl */\n"; 327 + print "/* Conversion constants for HZ == $hz */\n"; 328 + print "\n"; 329 + print "#ifndef KERNEL_TIMECONST_H\n"; 330 + print "#define KERNEL_TIMECONST_H\n"; 331 + print "\n"; 332 + 333 + print "#include <linux/param.h>\n"; 334 + 335 + print "\n"; 336 + print "#if HZ != $hz\n"; 337 + print "#error \"kernel/timeconst.h has the wrong HZ value!\"\n"; 338 + print "#endif\n"; 339 + print "\n"; 340 + 341 + foreach $pfx ('HZ_TO_MSEC','MSEC_TO_HZ', 342 + 'USEC_TO_HZ','HZ_TO_USEC') { 343 + foreach $bit (32, 64) { 344 + foreach $suf ('MUL', 'ADJ', 'SHR') { 345 + printf "#define %-23s %s\n", 346 + "${pfx}_$suf$bit", shift(@val); 347 + } 348 + } 349 + foreach $suf ('NUM', 'DEN') { 350 + printf "#define %-23s %s\n", 351 + "${pfx}_$suf", shift(@val); 352 + } 353 + } 354 + 355 + print "\n"; 356 + print "#endif /* KERNEL_TIMECONST_H */\n"; 357 + } 358 + 359 + ($hz) = @ARGV; 360 + 361 + # Use this to generate the %canned_values structure 362 + if ($hz eq '--can') { 363 + shift(@ARGV); 364 + @hzlist = sort {$a <=> $b} (@ARGV); 365 + 366 + print "# Precomputed values for systems without Math::BigInt\n"; 367 + print "# Generated by:\n"; 368 + print "# timeconst.pl --can ", join(' ', @hzlist), "\n"; 369 + print "\%canned_values = (\n"; 370 + my $pf = "\t"; 371 + foreach $hz (@hzlist) { 372 + my @values = compute_values($hz); 373 + print "$pf$hz => [\n"; 374 + while (scalar(@values)) { 375 + my $bit; 376 + foreach $bit (32, 64) { 377 + my $m = shift(@values); 378 + my $a = shift(@values); 379 + my $s = shift(@values); 380 + print "\t\t\'",$m,"\',\'",$a,"\',",$s,",\n"; 381 + } 382 + my $n = shift(@values); 383 + my $d = shift(@values); 384 + print "\t\t",$n,',',$d,",\n"; 385 + } 386 + print "\t]"; 387 + $pf = ', '; 388 + } 389 + print "\n);\n"; 390 + } else { 391 + $hz += 0; # Force to number 392 + if ($hz < 1) { 393 + die "Usage: $0 HZ\n"; 394 + } 395 + 396 + @val = @{$canned_values{$hz}}; 397 + if (!defined(@val)) { 398 + @val = compute_values($hz); 399 + } 400 + output($hz, @val); 401 + } 402 + exit 0;