[lkml]   [2004]   [Jun]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
Messages in this thread
    SubjectToo much error in __const_udelay() ?
    [Resending due to lkml bouncing me for having html attachments]

    Hey all,
    I've been hunting a bug on one of our systems and it seems to closely
    resemble the __delay() issues we saw w/ the ACPI PM time source.

    Earlier when implementing the ACPI PM time source, we found that we
    couldn't use the actual ACPI PM time source for the __delay function, or
    else various subsystems would break. I chalked it up to the 8Mhz counter
    being too low res for some callers of delay(), causing the waits to be
    far too long. We backed off to just using the TSC for delay() and things
    seemed to get better.

    However I've started to see some problems w/ 2.6 and USB on x440/x445s,
    both of which use the 100Mhz cyclone time source. Further digging has
    pointed to the fact that certain important udelay()s in the USB
    subsystem aren't actually waiting long enough.

    So far I've narrowed it down to the scaled math bits in
    __const_udelay() causing too much error for loops_per_jiffy values
    around the 100,000 level the cyclone timesource uses.

    To demonstrate this, I wrote the attached demo app using the
    __const_udelay code. For those not wanting to run it themselves, its
    output (for HZ=1000) looks like:

    1 usec: LPJ: 100000 __udelay: 0 vs my_udelay: 100
    1 usec: LPJ: 1500000 __udelay: 1000 vs my_udelay: 1500

    2 usec: LPJ: 100000 __udelay: 0 vs my_udelay: 200
    2 usec: LPJ: 1500000 __udelay: 2000 vs my_udelay: 3000

    5 usec: LPJ: 100000 __udelay: 0 vs my_udelay: 500
    5 usec: LPJ: 1500000 __udelay: 7000 vs my_udelay: 7500

    10 usec: LPJ: 100000 __udelay: 0 vs my_udelay: 1000
    10 usec: LPJ: 1500000 __udelay: 14000 vs my_udelay: 15000

    20 usec: LPJ: 100000 __udelay: 1000 vs my_udelay: 2000
    20 usec: LPJ: 1500000 __udelay: 29000 vs my_udelay: 30000

    50 usec: LPJ: 100000 __udelay: 4000 vs my_udelay: 5000
    50 usec: LPJ: 1500000 __udelay: 74000 vs my_udelay: 75000

    100 usec: LPJ: 100000 __udelay: 9000 vs my_udelay: 10000
    100 usec: LPJ: 1500000 __udelay: 149000 vs my_udelay: 150000

    Here you can see __udelay() fails to be even close to accurate until
    ~50usec and returns zero for values less then 20usec.

    I then went and measured the same udelay() values via rdtsc in the
    kernel for both the cyclone based delay() as well as the tsc based
    delay. The results are attached in the html file. You'll notice this
    closely matches the results from the demo app.

    This issue hasn't bitten me before w/ 2.4 because (as you can show w/
    the demo app) __const_udelay() is more accurate w/ HZ=100.

    I tried replacing __const_udelay w/ my_delay() but it didn't boot
    (overflow issues, I'm guessing).

    So I'm no math wiz. What's the proper fix here?

    #include <stdio.h>

    #define MILLION 1000000
    #define HZ 1000

    unsigned long tsc_freq = 1500000000UL;
    unsigned long cyclone_freq = 100000000UL;
    unsigned long tsc_lpj = 1500000000UL/HZ;
    unsigned long cyclone_lpj = 100000000UL/HZ;
    unsigned long LPJ;

    unsigned long __delay(unsigned long loops)
    return loops;

    unsigned long __const_udelay(unsigned long xloops)
    int d0;
    __asm__("mull %0"
    :"=d" (xloops), "=&a" (d0)
    :"1" (xloops),"0" (LPJ));
    return __delay(xloops * HZ);

    unsigned long my_udelay(unsigned long usec)
    unsigned long cyc_per_usec = (LPJ*HZ)/MILLION;
    return __delay(usec*cyc_per_usec);

    unsigned long __udelay(unsigned long usecs)
    return __const_udelay(usecs * 0x000010c6); /* 2**32 / 1000000 */

    unsigned long __ndelay(unsigned long nsecs)
    return __const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */

    int main(void)
    unsigned long time[7] = {1,2,5,10,20,50,100};
    int i;

    for(i=0;i < 7; i++){
    printf("%3i usec: LPJ: %7lu __udelay: %5lu vs my_udelay: %5lu\n",
    time[i], LPJ, __udelay(time[i]), my_udelay(time[i]));
    printf("%3i usec: LPJ: %lu __udelay: %5lu vs my_udelay: %5lu\n",
    time[i], LPJ, __udelay(time[i]), my_udelay(time[i]));

    return 0;
    [unhandled content-type:application/x-gzip]
     \ /
      Last update: 2005-03-22 14:03    [W:1.042 / U:0.024 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site