Tuesday, September 14, 2010

Problem with udelay

I was working on a mobile device, reducing lcd wake up time once powerkey is pressed. In LCD driver there were many msleeps() used in the kernel. There is some structure like this.

Struct lcd_startup_sequence {
U16 cmd;
U16 timeout;/*in ms*/
}

lcd_startup_sequence[10] = {
{0x34, 100},
{0x23, 0},
{0x14, 0},
{0x11, 50},
};

What i did first is this,

if(lcd_starup_sequence.delay)
msleep(lcd_startup_swquence.delay);

So this will avoids the calls from msleep(0); Because even if msleep is called with 0 as the argument, the current thread will be kept on the wait queue from the running queue.

But from this I haven't got much improvement. So I thought of changing msleep() to mdelay(). So it takes the correct time, because it is busy waiting than sleeping. But you should be very careful in using mdelay() because you are just eating up the processor time. So if not necessary use msleep() instead. This give me good improvement.

The funny part is that in another place also I changed from msleep(100) to mdelay(100). Surprisingly here mdelay is taking around 200 ms and msleep is taking 110 ms. What the heck?
Then I digged in the kernel and found plenty of information about kernel time keeping. The following link explaing how the delay is generated in linux kernel:
http://kerneltrap.org/node/6857

But the reason for mdelay behavior is explained below:
udelay() can be incorrect on SMP machines that scale their CPU frequencies independently of one another he delay loop can either be too fast or too slow depending on which CPU the loops_per_jiffy counter is calibrated on and which CPU the delay loop is running on. udelay() can also be incorrect if the CPU frequency switches during the __delay() loop, causing the loop
to either terminate too early, or too late.

For more informations:
http://www.spinics.net/lists/linux-arm-msm/msg00208.html
http://groups.google.co.kr/group/linux.kernel/browse_thread/thread/0a5c1395e82eabc2?pli=1