본문 바로가기

Core BSP 분석/리눅스 커널 핵심 분석

hrtimer_restart, alarm timer

This is a good chance to look into timer mechanism ofkernel timer.

/**
* alarmtimer_fired - Handles alarm hrtimer being fired.
* @timer: pointer to hrtimer being run
*
* When a alarm timer fires, this runs through the timerqueue to
* see which alarms expired, and runs those. If there are more alarm
* timers queued for the future, we set the hrtimer to fire when
* when the next future alarm timer expires.
*/
static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
{
    struct alarm_base *base = container_of(timer, struct alarm_base, timer);
    struct timerqueue_node *next;
    unsigned long flags;
    ktime_t now;
    int ret = HRTIMER_NORESTART;
    int restart = ALARMTIMER_NORESTART;
 
    spin_lock_irqsave(&base->lock, flags);
    now = base->gettime();
    while ((next = timerqueue_getnext(&base->timerqueue))) {
        struct alarm *alarm;
        ktime_t expired = next->expires;
 
        if (expired.tv64 > now.tv64)
            break;
 
        alarm = container_of(next, struct alarm, node);
 
        timerqueue_del(&base->timerqueue, &alarm->node);
        alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;
 
        alarm->state |= ALARMTIMER_STATE_CALLBACK;
        spin_unlock_irqrestore(&base->lock, flags);
        if (alarm->function)
            restart = alarm->function(alarm, now);
        spin_lock_irqsave(&base->lock, flags);
        alarm->state &= ~ALARMTIMER_STATE_CALLBACK;
 
        if (restart != ALARMTIMER_NORESTART) {
            timerqueue_add(&base->timerqueue, &alarm->node);
            alarm->state |= ALARMTIMER_STATE_ENQUEUED;
        }
    }
 
    if (next) {
        hrtimer_set_expires(&base->timer, next->expires);
        ret = HRTIMER_RESTART;
    }
    spin_unlock_irqrestore(&base->lock, flags);
 
    return ret;
 
}.

 

This is a good chance to look into timer mechanism ofkernel timer.

 

static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int rv = 0;
    unsigned long flags;
<snip>
case ANDROID_ALARM_SET_OLD:
    case ANDROID_ALARM_SET_AND_WAIT_OLD:
        if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
            rv = -EFAULT;
            goto err1;
        }
        new_alarm_time.tv_nsec = 0;
        goto from_old_alarm_set;
 
    case ANDROID_ALARM_SET_AND_WAIT(0):
    case ANDROID_ALARM_SET(0):
        if (copy_from_user(&new_alarm_time, (void __user *)arg,
            sizeof(new_alarm_time))) {
            rv = -EFAULT;
            goto err1;
        }
from_old_alarm_set:
        spin_lock_irqsave(&alarm_slock, flags);
        pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type,
            new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
        alarm_enabled |= alarm_type_mask;
        devalarm_start(&alarms[alarm_type],
            timespec_to_ktime(new_alarm_time));
        spin_unlock_irqrestore(&alarm_slock, flags);
        if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
            && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
            break;
        /* fall though */
    case ANDROID_ALARM_WAIT:

 

 

static const struct file_operations alarm_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = alarm_ioctl,
    .open = alarm_open,
    .release = alarm_release,
};

 

This driver is accessible thru /dev/alarm.

static struct miscdevice alarm_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "alarm",
    .fops = &alarm_fops,
};

 

 

[ 1248.598756 04-23 15:18:45.380] [<c00bccd4>] (alarmtimer_fired+0x144/0x15c) from [<c00970ac>] (__run_hrtimer+0x98/0x304)
[ 1248.609786 04-23 15:18:45.380] [<c00970ac>] (__run_hrtimer+0x98/0x304) from [<c0097fb0>] (hrtimer_interrupt+0x134/0x2f0)
[ 1248.620908 04-23 15:18:45.380] [<c0097fb0>] (hrtimer_interrupt+0x134/0x2f0) from [<c0028e84>] (tegra_cputimer_interrupt+0x4c/0x54)
[ 1248.632924 04-23 15:18:45.380] [<c0028e84>] (tegra_cputimer_interrupt+0x4c/0x54) from [<c00e9450>] (handle_irq_event_percpu+0x78/0x2d8)
[ 1248.645365 04-23 15:18:45.380] [<c00e9450>] (handle_irq_event_percpu+0x78/0x2d8) from [<c00e96f4>] (handle_irq_event+0x44/0x64)
[ 1248.657087 04-23 15:18:45.380] [<c00e96f4>] (handle_irq_event+0x44/0x64) from [<c00ec480>] (handle_fasteoi_irq+0xc4/0x16c)
[ 1248.668375 04-23 15:18:45.380] [<c00ec480>] (handle_fasteoi_irq+0xc4/0x16c) from [<c00e8c6c>] (generic_handle_irq+0x30/0x44)
[ 1248.679844 04-23 15:18:45.380] [<c00e8c6c>] (generic_handle_irq+0x30/0x44) from [<c000f074>] (handle_IRQ+0x54/0xb4)
[ 1248.690514 04-23 15:18:45.380] [<c000f074>] (handle_IRQ+0x54/0xb4) from [<c00084cc>] (gic_handle_irq+0x2c/0x60)
[ 1248.700859 04-23 15:18:45.380] [<c00084cc>] (gic_handle_irq+0x2c/0x60) from [<c081d804>] (__irq_svc+0x44/0x78)

 

static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
{
    struct hrtimer_clock_base *base = timer->base;
    struct hrtimer_cpu_base *cpu_base = base->cpu_base;
    enum hrtimer_restart (*fn)(struct hrtimer *);
    int restart;
 
    WARN_ON(!irqs_disabled());
 
    debug_deactivate(timer);
    __remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0);
    timer_stats_account_hrtimer(timer);
    fn = timer->function; // alarmtimer_fired is called thru this callback function.
<snip>