[S390] Calibrate delay and bogomips.

Preset the bogomips number to the cpu capacity value reported by
store system information in SYSIB 1.2.2. This value is constant
for a particular machine model and can be used to determine
relative performance differences between machines.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Martin Schwidefsky 2007-02-05 21:18:31 +01:00
parent 31cb4bd31a
commit 31ee4b2f40
11 changed files with 149 additions and 88 deletions

View File

@ -34,10 +34,6 @@ config GENERIC_HWEIGHT
bool
default y
config GENERIC_CALIBRATE_DELAY
bool
default y
config GENERIC_TIME
def_bool y

View File

@ -938,6 +938,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
struct cpuinfo_S390 *cpuinfo;
unsigned long n = (unsigned long) v - 1;
s390_adjust_jiffies();
preempt_disable();
if (!n) {
seq_printf(m, "vendor_id : IBM/S390\n"

View File

@ -4,7 +4,7 @@
EXTRA_AFLAGS := -traditional
lib-y += delay.o string.o uaccess_std.o uaccess_pt.o
lib-y += delay.o string.o uaccess_std.o uaccess_pt.o qrnnd.o
lib-$(CONFIG_32BIT) += div64.o
lib-$(CONFIG_64BIT) += uaccess_mvcos.o
lib-$(CONFIG_SMP) += spinlock.o

77
arch/s390/lib/qrnnd.S Normal file
View File

@ -0,0 +1,77 @@
# S/390 __udiv_qrnnd
# r2 : &__r
# r3 : upper half of 64 bit word n
# r4 : lower half of 64 bit word n
# r5 : divisor d
# the reminder r of the division is to be stored to &__r and
# the quotient q is to be returned
.text
.globl __udiv_qrnnd
__udiv_qrnnd:
st %r2,24(%r15) # store pointer to reminder for later
lr %r0,%r3 # reload n
lr %r1,%r4
ltr %r2,%r5 # reload and test divisor
jp 5f
# divisor >= 0x80000000
srdl %r0,2 # n/4
srl %r2,1 # d/2
slr %r1,%r2 # special case if last bit of d is set
brc 3,0f # (n/4) div (n/2) can overflow by 1
ahi %r0,-1 # trick: subtract n/2, then divide
0: dr %r0,%r2 # signed division
ahi %r1,1 # trick part 2: add 1 to the quotient
# now (n >> 2) = (d >> 1) * %r1 + %r0
lhi %r3,1
nr %r3,%r1 # test last bit of q
jz 1f
alr %r0,%r2 # add (d>>1) to r
1: srl %r1,1 # q >>= 1
# now (n >> 2) = (d&-2) * %r1 + %r0
lhi %r3,1
nr %r3,%r5 # test last bit of d
jz 2f
slr %r0,%r1 # r -= q
brc 3,2f # borrow ?
alr %r0,%r5 # r += d
ahi %r1,-1
2: # now (n >> 2) = d * %r1 + %r0
alr %r1,%r1 # q <<= 1
alr %r0,%r0 # r <<= 1
brc 12,3f # overflow on r ?
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
3: lhi %r3,2
nr %r3,%r4 # test next to last bit of n
jz 4f
ahi %r0,1 # r += 1
4: clr %r0,%r5 # r >= d ?
jl 6f
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
# now (n >> 1) = d * %r1 + %r0
j 6f
5: # divisor < 0x80000000
srdl %r0,1
dr %r0,%r2 # signed division
# now (n >> 1) = d * %r1 + %r0
6: alr %r1,%r1 # q <<= 1
alr %r0,%r0 # r <<= 1
brc 12,7f # overflow on r ?
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
7: lhi %r3,1
nr %r3,%r4 # isolate last bit of n
alr %r0,%r3 # r += (n & 1)
clr %r0,%r5 # r >= d ?
jl 8f
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
8: # now n = d * %r1 + %r0
l %r2,24(%r15)
st %r0,0(%r2)
lr %r2,%r1
br %r14
.end __udiv_qrnnd

View File

@ -2,7 +2,7 @@
# Makefile for the FPU instruction emulation.
#
obj-$(CONFIG_MATHEMU) := math.o qrnnd.o
obj-$(CONFIG_MATHEMU) := math.o
EXTRA_CFLAGS := -I$(src) -Iinclude/math-emu -w
EXTRA_AFLAGS := -traditional

View File

@ -15,7 +15,7 @@
#include <asm/uaccess.h>
#include <asm/lowcore.h>
#include "sfp-util.h"
#include <asm/sfp-util.h>
#include <math-emu/soft-fp.h>
#include <math-emu/single.h>
#include <math-emu/double.h>

View File

@ -1,77 +0,0 @@
# S/390 __udiv_qrnnd
# r2 : &__r
# r3 : upper half of 64 bit word n
# r4 : lower half of 64 bit word n
# r5 : divisor d
# the reminder r of the division is to be stored to &__r and
# the quotient q is to be returned
.text
.globl __udiv_qrnnd
__udiv_qrnnd:
st %r2,24(%r15) # store pointer to reminder for later
lr %r0,%r3 # reload n
lr %r1,%r4
ltr %r2,%r5 # reload and test divisor
jp 5f
# divisor >= 0x80000000
srdl %r0,2 # n/4
srl %r2,1 # d/2
slr %r1,%r2 # special case if last bit of d is set
brc 3,0f # (n/4) div (n/2) can overflow by 1
ahi %r0,-1 # trick: subtract n/2, then divide
0: dr %r0,%r2 # signed division
ahi %r1,1 # trick part 2: add 1 to the quotient
# now (n >> 2) = (d >> 1) * %r1 + %r0
lhi %r3,1
nr %r3,%r1 # test last bit of q
jz 1f
alr %r0,%r2 # add (d>>1) to r
1: srl %r1,1 # q >>= 1
# now (n >> 2) = (d&-2) * %r1 + %r0
lhi %r3,1
nr %r3,%r5 # test last bit of d
jz 2f
slr %r0,%r1 # r -= q
brc 3,2f # borrow ?
alr %r0,%r5 # r += d
ahi %r1,-1
2: # now (n >> 2) = d * %r1 + %r0
alr %r1,%r1 # q <<= 1
alr %r0,%r0 # r <<= 1
brc 12,3f # overflow on r ?
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
3: lhi %r3,2
nr %r3,%r4 # test next to last bit of n
jz 4f
ahi %r0,1 # r += 1
4: clr %r0,%r5 # r >= d ?
jl 6f
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
# now (n >> 1) = d * %r1 + %r0
j 6f
5: # divisor < 0x80000000
srdl %r0,1
dr %r0,%r2 # signed division
# now (n >> 1) = d * %r1 + %r0
6: alr %r1,%r1 # q <<= 1
alr %r0,%r0 # r <<= 1
brc 12,7f # overflow on r ?
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
7: lhi %r3,1
nr %r3,%r4 # isolate last bit of n
alr %r0,%r3 # r += (n & 1)
clr %r0,%r5 # r >= d ?
jl 8f
slr %r0,%r5 # r -= d
ahi %r1,1 # q += 1
8: # now n = d * %r1 + %r0
l %r2,24(%r15)
st %r0,0(%r2)
lr %r2,%r1
br %r14
.end __udiv_qrnnd

View File

@ -2,6 +2,8 @@
# Makefile for the S/390 specific device drivers
#
CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
obj-y += s390mach.o sysinfo.o s390_rdev.o
obj-y += cio/ block/ char/ crypto/ net/ scsi/

View File

@ -9,8 +9,14 @@
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/ebcdic.h>
/* Sigh, math-emu. Don't ask. */
#include <asm/sfp-util.h>
#include <math-emu/soft-fp.h>
#include <math-emu/single.h>
struct sysinfo_1_1_1 {
char reserved_0[32];
char manufacturer[16];
@ -198,7 +204,7 @@ static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len)
* if the higher order 8 bits are not zero. Printing
* a floating point number in the kernel is a no-no,
* always print the number as 32 bit unsigned integer.
* The user-space needs to know about the stange
* The user-space needs to know about the strange
* encoding of the alternate cpu capability.
*/
len += sprintf(page + len, "Capability: %u %u\n",
@ -351,3 +357,58 @@ static __init int create_proc_sysinfo(void)
__initcall(create_proc_sysinfo);
/*
* CPU capability might have changed. Therefore recalculate loops_per_jiffy.
*/
void s390_adjust_jiffies(void)
{
struct sysinfo_1_2_2 *info;
const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */
FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
FP_DECL_EX;
unsigned int capability;
info = (void *) get_zeroed_page(GFP_KERNEL);
if (!info)
return;
if (stsi(info, 1, 2, 2) != -ENOSYS) {
/*
* Major sigh. The cpu capability encoding is "special".
* If the first 9 bits of info->capability are 0 then it
* is a 32 bit unsigned integer in the range 0 .. 2^23.
* If the first 9 bits are != 0 then it is a 32 bit float.
* In addition a lower value indicates a proportionally
* higher cpu capacity. Bogomips are the other way round.
* To get to a halfway suitable number we divide 1e7
* by the cpu capability number. Yes, that means a floating
* point division .. math-emu here we come :-)
*/
FP_UNPACK_SP(SA, &fmil);
if ((info->capability >> 23) == 0)
FP_FROM_INT_S(SB, info->capability, 32, int);
else
FP_UNPACK_SP(SB, &info->capability);
FP_DIV_S(SR, SA, SB);
FP_TO_INT_S(capability, SR, 32, 0);
} else
/*
* Really old machine without stsi block for basic
* cpu information. Report 42.0 bogomips.
*/
capability = 42;
loops_per_jiffy = capability * (500000/HZ);
free_page((unsigned long) info);
}
/*
* calibrate the delay loop
*/
void __init calibrate_delay(void)
{
s390_adjust_jiffies();
/* Print the good old Bogomips line .. */
printk(KERN_DEBUG "Calibrating delay loop (skipped)... "
"%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
}

View File

@ -50,6 +50,7 @@ struct cpuinfo_S390
unsigned long pgtable_cache_sz;
};
extern void s390_adjust_jiffies(void);
extern void print_cpu_info(struct cpuinfo_S390 *);
/* Lazy FPU handling on uni-processor */

View File

@ -52,12 +52,12 @@
})
#define udiv_qrnnd(q, r, n1, n0, d) \
do { unsigned long __r; \
do { unsigned int __r; \
(q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
(r) = __r; \
} while (0)
extern unsigned long __udiv_qrnnd (unsigned long *, unsigned long,
unsigned long , unsigned long);
extern unsigned long __udiv_qrnnd (unsigned int *, unsigned int,
unsigned int , unsigned int);
#define UDIV_NEEDS_NORMALIZATION 0