Question

Though this topic has been discussed many times in this forum and all other forums, still I have doubts. Please help.

How does the do{} while(0) in macro work in Linux kernel? For example,

#define preempt_disable()    do { } while (0)

How does it disable preempt?

#define might_resched()    do { } while (0)

How does it reschedule?

Similarly I have seen macros for mutex locks and other also. How does this help? I understand for following problem but not for the examples above.

#define foo(x)    do { do something } while(0)

Edit:

What about the following code for rt_mutex_lock?

/**
 * rt_mutex_lock - lock a rt_mutex
 *
 * @lock: the rt_mutex to be locked
 */
void __sched rt_mutex_lock(struct rt_mutex *lock)
{
        might_sleep();
        rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, 0, rt_mutex_slowlock);
}
EXPORT_SYMBOL_GPL(rt_mutex_lock);


/*
 * debug aware fast / slowpath lock,trylock,unlock
 *
 * The atomic acquire/release ops are compiled away, when either the
 * architecture does not support cmpxchg or when debugging is enabled.
 */

static inline int rt_mutex_fastlock(struct rt_mutex *lock, 
    int state, int detect_deadlock, int (*slowfn)(struct rt_mutex *lock, 
    int state, struct hrtimer_sleeper *timeout, int detect_deadlock))
{
        if (!detect_deadlock && likely(rt_mutex_cmpxchg(lock, NULL, current))) {
                rt_mutex_deadlock_account_lock(lock, current);
                return 0;
        } else{
                return slowfn(lock, state, NULL, detect_deadlock);
        }
}

I am confused because rt_mutex_deadlock_account_lock is define at two places in the kernel:

In kernel/rtmutex-debug.c:

void rt_mutex_deadlock_account_lock(struct rt_mutex *lock, 
    struct task_struct *task)
{
    //....
}

In kernel/rtmutex.h:

#define rt_mutex_deadlock_account_lock(m, t) do { } while (0)

In new kernel 2.6.35.4 in the i2c driver rt_mutex_lock(&adap->bus_lock); has replaced the mutex_lock(). How does this lock then?

Was it helpful?

Solution

@Kragen has answered what the do...while construct is for - it basically makes a macro much safer to use.

However, I don't think it answers the question of "how does this work?":

#define preempt_disable()    do { } while (0)

The macro is defined to do nothing. Why would you want to do nothing?

  • In some cases you want to use a macro as a placeholder for doing something. For example, you might write code on one system where "preempt" isn't an issue, but you know the code might be ported to a system where "preempt" needs some special handling. So you use a macro everywhere the second system needs it (so that the handling is easy to enable later), but for the first system you then define that macro as a blank macro.

  • In some cases you may want to do things like a task that is made up of different parts, (e.g. START_TABLE(); TABLE_ENTRY(1); TABLE_ENTRY(2); END_TABLE();). This makes a nice clean clear implementation of your table. But then you find that you don't actually need the END_TABLE() macro. To keep the client code tidy, you leave the macro defined, and simply define it to do nothing. That way, all your tables have an END_TABLE and the code is easier to read.

  • A similar case can occur with two states (enable/disable) where one state needs the macro to do something, but the other state just happens by default, so the implementation of one is "empty" - you still use the macro because it makes the client code easier to understand, because it explicitly states the places where things are enabled or disabled.

OTHER TIPS

See this link for a better explanation than I could give.

IIRC the use of the do-while in macros is to make them look more like a normal function invocation; there are some subtle syntax issues around unbraced if statements and things like that. Without the do-while the macro might look like a normal function invocation but would work differently.

I would guess that in this case those macros are being used so certain function calls compile away to nothing; it looks like that might be what you get if CONFIG_PREEMPT wasn't set, so certain parts of the kernel that are only necessary for preempt simply vanish without it. So those loops do not disable preempt or reschedule anything; there'll be another definition (probably a real function) elsewhere in the kernel source.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top