您的位置:首页 > 其它

Reading notes of Dynamic Ticks

2011-11-27 23:32 423 查看
================================

Most of the contents is from the book, but as it based on 2.6 kernel.

I analysis the a new version 3.1.1. So there are some changes.

====================================


Dynamic Ticks:



contents:

0. Introductions

1. Dynamic Ticks for Low-Resolution Systems

2. The Dynamic Tick Handler

3. Updating jiffies

4. Dynamic Ticks for High-Resolution Systems

5. Stopping and Starting Periodic Ticks

==================

0. Introductions

Periodic ticks have provided a notion of time to the Linux kernel for many of years.

The approach is simple and effective, but show one particular deficiency on systems

where power consumption does matter: The periodic tick requires that the system

is in an active state at a certain frequency. Longer periods of rest are impossible

because of this.

Dynamic ticks mend this problem. The periodic tick is only activated when some tasks

actually do need to be peformend.

How can the kernel decide if the systemm has nothing to do? If no active tasks

are on the run queue, the kernel picks a special task, the idle task, to run. At this point,

the dynamic tick mechanism enters the game. Whenever the idle task is selected

to run, the periodic tick is disabled until the next timer will expire. The tick is

re-enabled again after this time span, or when an interrupt occurs. In the meantime,

the CPU can enjoy a well-deserved sleep.

!!! NOTE that only classical timers need to be considered for this purpose.

High-resolution timers are not bound by the tick frequency, and are also not

implemented on top of periodic ticks.

The following are the data structures related.


enum tick_nohz_mode {

NOHZ_MODE_INACTIVE,// periodic ticks are active

NOHZ_MODE_LOWRES, //dynamic ticks are used based on low-resolution mode

NOHZ_MODE_HIGHRES,// dynamic ticks are used based on high-resolution mode

};

/**

* struct tick_sched - sched tick emulation and no idle tick control/stats

* @sched_timer: hrtimer to schedule the periodic tick in high

* resolution mode

* @idle_tick: Store the last idle tick expiry time when the tick

* timer is modified for idle sleeps. This is necessary

* to resume the tick timer operation in the timeline

* when the CPU returns from idle

// A sufficient number of tick intervals are added to obtain the expiration time

// for the next tick.

* @tick_stopped: Indicator that the idle tick has been stopped

* @idle_jiffies: jiffies at the entry to idle for idle time accounting +

* @idle_calls: Total number of idle calls

* @idle_sleeps: Number of idle calls, where the sched tick was stopped

* @idle_entrytime: Time when the idle call was entered

* @idle_waketime: Time when the idle was interrupted

* @idle_exittime: Time when the idle state was left

* @idle_sleeptime: Sum of the time slept in idle with sched tick stopped

* @iowait_sleeptime: Sum of the time slept in idle with sched tick stopped, with IO outstanding

* @sleep_length: Duration of the current idle sleep

* @do_timer_lst: CPU was the last one doing do_timer before going idle

*/

struct tick_sched {

struct hrtimer sched_timer;

unsigned long check_clocks;

/*

* The current mode of operation.

*/

enum tick_nohz_mode nohz_mode;

ktime_t idle_tick;

int inidle;

int tick_stopped;

unsigned long idle_jiffies;

unsigned long idle_calls;

unsigned long idle_sleeps;

int idle_active;

ktime_t idle_entrytime;

ktime_t idle_waketime;

ktime_t idle_exittime;

ktime_t idle_sleeptime;

ktime_t iowait_sleeptime;

ktime_t sleep_length;

unsigned long last_jiffies;

unsigned long next_jiffies;

ktime_t idle_expires;

int do_timer_last;

};

@tick_cpu_sched is a global per-CPU variable that provides an instance of

struct tick_sched. This is required because disabling ticks naturally works

per CPU, not globally for the whole system.

1. Dynamic Ticks for Low-Resolution Systems

Consider the situation in which the kernel does not use high-resolution timers

and provides only low resolution. We will show how are dynamic ticks implemented in this

scenario.

-- Switching to Dynamic Ticks

Recall that, in the Generic Time Substem, tick_setup_device() is used to

set up a tick device. If the clock event device supports periodic events,

tick_setup_periodic() installs tick_handle_periodic() as handler function of

the tick device. tick_handle_periodic() is called on the next event of the tick

device. tick_periodic() is called in tick_handle_periodic(). tick_periodic() is

responsible for handling the perioc tick on a given CPU required as an

argument. The following is the call tree.



Call Tree:

tick_handle_periodic

tick_periodic

Call Tree:

tick_periodic | tick_nohz_handler | tick_sched_timer

update_process_times

run_local_timers

hrtimer_run_queues

raise_softirq(TIMER_SOFTIRQ) // This will trigger the excuting of

// run_timer_softirq()

Call Tree:

run_timer_softirq

hrtimer_run_pending

tick_check_oneshot_change //****

hrtimer_switch_to_hres

From the call tree above, we know that every time update_local_timers() called,

run_timer_softirq() will be called. In run_timer_softirq(), hrtimer_run_pending is called.

hrtimer_run_queues() calles tick_check_oneshot_change() to decide if high-resolution

timers can be activated. Additionally, the function checks if dynamic ticks can be

enabled on low-resolution systems. This is possible under two conditions:

1> A clock event device that supports one-shot mode is available.

2> high-resolution is not enabled.


1405 /*

1406 * Called from timer softirq every jiffy, expire hrtimers:

1407 *

1408 * For HRT its the fall back code to run the softirq in the timer

1409 * softirq context in case the hrtimer initialization failed or has

1410 * not been done yet.

1411 */

1412 void hrtimer_run_pending(void)

1413 {

1414 if (hrtimer_hres_active())

1415 return;

1416

1417 /*

1418 * This _is_ ugly: We have to check in the softirq context,

1419 * whether we can switch to highres and / or nohz mode. The

1420 * clocksource switch happens in the timer interrupt with

1421 * xtime_lock held. Notification from there only sets the

1422 * check bit in the tick_oneshot code, otherwise we might

1423 * deadlock vs. xtime_lock.

1424 */

1425 if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))

1426 hrtimer_switch_to_hres();

1427 }

839 /**

840 * Check, if a change happened, which makes oneshot possible.

841 *

842 * Called cyclic from the hrtimer softirq (driven by the timer

843 * softirq) allow_nohz signals, that we can switch into low-res nohz

844 * mode, because high resolution timers are disabled (either compile

845 * or runtime).

846 */

847 int tick_check_oneshot_change(int allow_nohz)

848 {

849 struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);

850

851 if (!test_and_clear_bit(0, &ts->check_clocks))

852 return 0;

853

854 if (ts->nohz_mode != NOHZ_MODE_INACTIVE)

855 return 0;

856

857 if (!timekeeping_valid_for_hres() || !tick_is_oneshot_available())

858 return 0;

859

860 if (!allow_nohz)

861 return 1;

862

863 tick_nohz_switch_to_nohz();

864 return 0;

865 }

46 /**

47 * tick_is_oneshot_available - check for a oneshot capable event device

48 */

49 int tick_is_oneshot_available(void)

50 {

51 struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);

52

53 if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT))

54 return 0;

55 if (!(dev->features & CLOCK_EVT_FEAT_C3STOP))

56 return 1;

57 return tick_broadcast_oneshot_available();

58 }



Call Tree:

hrtimer_run_pending

tick_nohz_switch_to_nohz

tick_switch_to_oneshot(tick_nohz_handler)

tick_broadcast_switch_to_oneshot // omit this now

hrtimer_init

tick_init_jiffy_update

hrtimer_set_expires

tick_program_event


609 /**

610 * tick_nohz_switch_to_nohz - switch to nohz mode

611 */

612 static void tick_nohz_switch_to_nohz(void)

613 {

614 struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);

615 ktime_t next;

616

/* If CONFIG_NO_HZ is set, tick nohz is enabled by default. */

617 if (!tick_nohz_enabled)

618 return;

619

620 local_irq_disable();

/*

* set @tick_cpu_device's event device's mode to TICKDEV_MODE_ONESHOT,

* change the event device's event_handler to tick_nohz_handler().

*/

621 if (tick_switch_to_oneshot(tick_nohz_handler)) {

622 local_irq_enable();

623 return;

624 }

625

626 ts->nohz_mode = NOHZ_MODE_LOWRES;

627

628 /*

629 * Recycle the hrtimer in ts, so we can share the

630 * hrtimer_forward with the highres code.

631 */

632 hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);

633 /* Get the next period */

634 next = tick_init_jiffy_update();

635

/*

* To get things going, the kernel finally needs to activate the first periodic

* tick by setting the timer to expire at the point in time when the next

* periodic tick would have been due.

*/

636 for (;;) {

637 hrtimer_set_expires(&ts->sched_timer, next);

638 if (!tick_program_event(next, 0))

639 break;

640 next = ktime_add(next, tick_period);

641 }

642 local_irq_enable();

643

644 printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", smp_processor_id());

645 }

646

101 /*

102 * NOHZ - aka dynamic tick functionality

103 */

104 #ifdef CONFIG_NO_HZ

105 /*

106 * NO HZ enabled ?

107 */

108 static int tick_nohz_enabled __read_mostly = 1;

109

110 /*

111 * Enable / Disable tickless mode

112 */

113 static int __init setup_tick_nohz(char *str)

114 {

115 if (!strcmp(str, "off"))

116 tick_nohz_enabled = 0;

117 else if (!strcmp(str, "on"))

118 tick_nohz_enabled = 1;

119 else

120 return 0;

121 return 1;

122 }

123

124 __setup("nohz=", setup_tick_nohz);

126 /**

127 * tick_switch_to_oneshot - switch to oneshot mode

128 */

/*

* set @tick_cpu_device's event device's mode to TICKDEV_MODE_ONESHOT,

* change the event device's event_handler to @handler.

*/

129 int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))

130 {

131 struct tick_device *td = &__get_cpu_var(tick_cpu_device);

132 struct clock_event_device *dev = td->evtdev;

133

134 if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||

135 !tick_device_is_functional(dev)) {

136

137 printk(KERN_INFO "Clockevents: "

138 "could not switch to one-shot mode:");

139 if (!dev) {

140 printk(" no tick device\n");

141 } else {

142 if (!tick_device_is_functional(dev))

143 printk(" %s is not functional.\n", dev->name);

144 else

145 printk(" %s does not support one-shot mode.\n",

146 dev->name);

147 }

148 return -EINVAL;

149 }

150

151 td->mode = TICKDEV_MODE_ONESHOT;

152 dev->event_handler = handler;

153 clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);

154 tick_broadcast_switch_to_oneshot();

155 return 0;

156 }

2. The Dynamic Tick Handler



The new tick handler tick_nohz_handler() needs to assume two responsibilities:

1> Perform all actions required for the tick mechanism.

2> Reprogram the tick device such that the next tick expires at the right time.


-------------------------------------------------------

Call Tree:

tick_periodic | tick_nohz_handler | tick_sched_timer

update_process_times

run_local_timers

hrtimer_run_queues

raise_softirq(TIMER_SOFTIRQ) // This will trigger the excuting of

// run_timer_softirq()

Call Tree:

run_timer_softirq

hrtimer_run_pending

tick_check_oneshot_change //****

hrtimer_switch_to_hres

Call Tree: // ****

tick_nohz_handler

tick_do_update_jiffies64

update_process_times

profile_tick

tick_nohz_reprogram



561 /*

562 * The nohz low res interrupt handler

563 */

564 static void tick_nohz_handler(struct clock_event_device *dev)

565 {

566 struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);

567 struct pt_regs *regs = get_irq_regs();

568 int cpu = smp_processor_id();

569 ktime_t now = ktime_get();

570

/*

* This ensures that the event device will not expire anytime soon, or

* never for practical purpose.

*/

571 dev->next_event.tv64 = KTIME_MAX;

572

573 /*

574 * Check if the do_timer duty was dropped. We don't care about

575 * concurrency: This happens only when the cpu in charge went

576 * into a long sleep. If two cpus happen to assign themself to

577 * this duty, then the jiffies update is still serialized by

578 * xtime_lock.

579 */

/*

* The role of the global tick device is assumed by one particular CPU,

* and the handler needs to check if the current CPU is the responsible one.

* If a CPU goes into a long sleep, then it cannot be responsible for the

* global tick anymore, and drops the duty. If this is the case, the next

* CPU handler is called must assume the duty.

*/

580 if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))

581 tick_do_timer_cpu = cpu;

582

583 /* Check, if the jiffies need an update */

/*

* If the CPU is responsible to provide the globale tick, it is sufficent to

* call tick_do_update_jiffies64() which will be talked later.

*/

584 if (tick_do_timer_cpu == cpu)

585 tick_do_update_jiffies64(now);

586

587 /*

588 * When we are idle and the tick is stopped, we have to touch

589 * the watchdog as we might not schedule for a really long

590 * time. This happens on complete idle SMP systems while

591 * waiting on the login prompt. We also increment the "start

592 * of idle" jiffy stamp so the idle accounting adjustment we

593 * do when we go busy again does not account too much ticks.

594 */

595 if (ts->tick_stopped) {

596 touch_softlockup_watchdog();

597 ts->idle_jiffies++;

598 }

599

/*

* Take over the duty of the local tick.

*/

600 update_process_times(user_mode(regs));

601 profile_tick(CPU_PROFILING);

602

/*

* Sets the tick timer to expire at the next jiffy.

* The while loop ensures that reprogramming is repeated until it

* succeeds if the procceing should have taken too long and the next

* tick lies already in the past.

*/

603 while (tick_nohz_reprogram(ts, now)) {

604 now = ktime_get();

605 tick_do_update_jiffies64(now);

606 }

607 }

608

3. Updating jiffies

The global tick device calls tick_do_update_jiffies64() to update the global

jiffies_64 variable, the basis of low-resolution timer handling. When periodic

ticks are in use, this is comparantively simple because the function is called

whenever a jiffy has passed. When dynamic ticks are enabled, the situation

ca arise in which all CPUs of the system are idle and none provides global

ticks. This needs to be taken into account by tick_do_update_jiffies64.



43 /*

44 * Must be called with interrupts disabled !

45 */

46 static void tick_do_update_jiffies64(ktime_t now)

47 {

48 unsigned long ticks = 0;

49 ktime_t delta;

50

51 /*

52 * Do a quick check without holding xtime_lock:

53 */

54 delta = ktime_sub(now, last_jiffies_update);

55 if (delta.tv64 < tick_period.tv64)

56 return;

57

58 /* Reevalute with xtime_lock held */

59 write_seqlock(&xtime_lock);

60

61 delta = ktime_sub(now, last_jiffies_update);

/*

* Updating the jiffies value is naturally only required if the last update

* is more than one tick period ago.

*/

62 if (delta.tv64 >= tick_period.tv64) {

63

64 delta = ktime_sub(delta, tick_period);

65 last_jiffies_update = ktime_add(last_jiffies_update,

66 tick_period);

67

68 /* Slow path for long timeouts */

69 if (unlikely(delta.tv64 >= tick_period.tv64)) {

70 s64 incr = ktime_to_ns(tick_period);

71

72 ticks = ktime_divns(delta, incr);

73

74 last_jiffies_update = ktime_add_ns(last_jiffies_update,

75 incr * ticks);

76 }

/*

* Update the global jiffies value.

*/

77 do_timer(++ticks);

78

79 /* Keep the tick_next_period variable up to date */

80 tick_next_period = ktime_add(last_jiffies_update, tick_period);

81 }

82 write_sequnlock(&xtime_lock);

83 }

84

4. Dynamic Ticks for High-Resolution Systems

Clock event devices run in one-shot mode anyway if the kernel used high

timer resolution, support for dynamic ticks is much easier to implement

than in the low-resolution case. The periodic tick is emulated by tick_sched_timer()

as discussed above. The function is also used to implement dynamic ticks.


710 /*

711 * High resolution timer specific code

712 */

713 #ifdef CONFIG_HIGH_RES_TIMERS

714 /*

715 * We rearm the timer until we get disabled by the idle code.

716 * Called with interrupts disabled and timer->base->cpu_base->lock held.

717 */

718 static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)

...

726 #ifdef CONFIG_NO_HZ

727 /*

728 * Check if the do_timer duty was dropped. We don't care about

729 * concurrency: This happens only when the cpu in charge went

730 * into a long sleep. If two cpus happen to assign themself to

731 * this duty, then the jiffies update is still serialized by

732 * xtime_lock.

733 */

734 if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))

735 tick_do_timer_cpu = cpu;

736 #endif

...

}

Recall that tick_setup_sched_timer() is used to initialize the tick emulation

layer for high-resolution systmes. If dynamic ticks are enabled at compile

time, a short piece of code is added to the function:



768 /**

769 * tick_setup_sched_timer - setup the tick emulation timer

770 */

771 void tick_setup_sched_timer(void)

...

795 #ifdef CONFIG_NO_HZ

796 if (tick_nohz_enabled) {

797 ts->nohz_mode = NOHZ_MODE_HIGHRES;

798 printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", smp_processor_id());

799 }

800 #endif

5. Stopping and Starting Periodic Ticks



Dynamic ticks provide the framework to defer periodic ticks for a while.

What the kernel still needs to decide is when ticks are supported to be

stopped and restarted.

A natural possibility to stop ticks is when the idle task is scheduled:

This proves that the proccessor really does not have anything better to do.

The idle task is implemented in an architecutre-specific way, and not

all architectures have been updated to support disabling the periodic tick

yet. Architectures differ in some details, but the genral principle is the

same.


void cpu_idle(void)

{

...

/* endless idle loop with no priority at all */

while (1) {

tick_nohz_stop_sched_tick(1);

while (!need_resched()) {

...

if (cpuidle_idle_call())

pm_idle();

...

}

...

tick_nohz_restart_sched_tick();

...

}

}

After calling tick_nohz_stop_sched_tick() to turn off ticks, the system goes into

an endless loop that ends when a process is available to be scheduled on the

processor. Ticks are then necessary again, and are reactivated by

tick_nohz_restart_sched_tick().

Two conditions can thus require restarting ticks:

1> An external interrupt make a process runnable, which requires the ticks

mechanism to work. In this case, ticks need to be resumed earlier than

initially planned.

2> The next tick event is due, and the clock interrupt signals that the time

for this has come. In this case, the ticks mechanism is resummed as planned

before.

Essentially, tick_nohz_stop_sched_tick() needs to perform three tasks:

1> Check if the next timer wheel event is more than one tick away.

2> If this is the case, reprogram the tick device to omit the next tick only

when it is necessary again. This automatically omits all ticks that are not required

3> Update the statistical information in tick_sched().


/**

* tick_nohz_stop_sched_tick - stop the idle tick from the idle task

*

* When the next event is more than a tick into the future, stop the idle tick

* Called either from the idle loop or from irq_exit() when an idle period was

* just interrupted by an interrupt which did not cause a reschedule.

*/

void tick_nohz_stop_sched_tick(int inidle)

{

unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags;

struct tick_sched *ts;

ktime_t last_update, expires, now;

struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;

u64 time_delta;

int cpu;

local_irq_save(flags);

cpu = smp_processor_id();

ts = &per_cpu(tick_cpu_sched, cpu);

/*

* Call to tick_nohz_start_idle stops the last_update_time from being

* updated. Thus, it must not be called in the event we are called from

* irq_exit() with the prior state different than idle.

*/

if (!inidle && !ts->inidle)

goto end;

/*

* Set ts->inidle unconditionally. Even if the system did not

* switch to NOHZ mode the cpu frequency governers rely on the

* update of the idle time accounting in tick_nohz_start_idle().

*/

ts->inidle = 1;

now = tick_nohz_start_idle(cpu, ts);

/*

* If this cpu is offline and it is the one which updates

* jiffies, then give up the assignment and let it be taken by

* the cpu which runs the tick timer next. If we don't drop

* this here the jiffies might be stale and do_timer() never

* invoked.

*/

if (unlikely(!cpu_online(cpu))) {

if (cpu == tick_do_timer_cpu)

tick_do_timer_cpu = TICK_DO_TIMER_NONE;

}

if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE))

goto end;

if (need_resched())

goto end;

if (unlikely(local_softirq_pending() && cpu_online(cpu))) {

static int ratelimit;

if (ratelimit < 10) {

printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n",

(unsigned int) local_softirq_pending());

ratelimit++;

}

goto end;

}

ts->idle_calls++;

/* Read jiffies and the time when jiffies were updated last */

do {

seq = read_seqbegin(&xtime_lock);

last_update = last_jiffies_update;

last_jiffies = jiffies;

time_delta = timekeeping_max_deferment();

} while (read_seqretry(&xtime_lock, seq));

if (rcu_needs_cpu(cpu) || printk_needs_cpu(cpu) ||

arch_needs_cpu(cpu)) {

next_jiffies = last_jiffies + 1;

delta_jiffies = 1;

} else {

/* Get the next timer wheel timer */

next_jiffies = get_next_timer_interrupt(last_jiffies);

delta_jiffies = next_jiffies - last_jiffies;

}

/*

* Do not stop the tick, if we are only one off

* or if the cpu is required for rcu

*/

if (!ts->tick_stopped && delta_jiffies == 1)

goto out;

/* Schedule the tick, if we are at least one jiffie off */

if ((long)delta_jiffies >= 1) {

/*

* If this cpu is the one which updates jiffies, then

* give up the assignment and let it be taken by the

* cpu which runs the tick timer next, which might be

* this cpu as well. If we don't drop this here the

* jiffies might be stale and do_timer() never

* invoked. Keep track of the fact that it was the one

* which had the do_timer() duty last. If this cpu is

* the one which had the do_timer() duty last, we

* limit the sleep time to the timekeeping

* max_deferement value which we retrieved

* above. Otherwise we can sleep as long as we want.

*/

if (cpu == tick_do_timer_cpu) {

tick_do_timer_cpu = TICK_DO_TIMER_NONE;

ts->do_timer_last = 1;

} else if (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {

time_delta = KTIME_MAX;

ts->do_timer_last = 0;

} else if (!ts->do_timer_last) {

time_delta = KTIME_MAX;

}

/*

* calculate the expiry time for the next timer wheel

* timer. delta_jiffies >= NEXT_TIMER_MAX_DELTA signals

* that there is no timer pending or at least extremely

* far into the future (12 days for HZ=1000). In this

* case we set the expiry to the end of time.

*/

if (likely(delta_jiffies < NEXT_TIMER_MAX_DELTA)) {

/*

* Calculate the time delta for the next timer event.

* If the time delta exceeds the maximum time delta

* permitted by the current clocksource then adjust

* the time delta accordingly to ensure the

* clocksource does not wrap.

*/

time_delta = min_t(u64, time_delta,

tick_period.tv64 * delta_jiffies);

}

if (time_delta < KTIME_MAX)

expires = ktime_add_ns(last_update, time_delta);

else

expires.tv64 = KTIME_MAX;

/* Skip reprogram of event if its not changed */

if (ts->tick_stopped && ktime_equal(expires, dev->next_event))

goto out;

/*

* nohz_stop_sched_tick can be called several times before

* the nohz_restart_sched_tick is called. This happens when

* interrupts arrive which do not cause a reschedule. In the

* first call we save the current tick time, so we can restart

* the scheduler tick in nohz_restart_sched_tick.

*/

if (!ts->tick_stopped) {

select_nohz_load_balancer(1);

ts->idle_tick = hrtimer_get_expires(&ts->sched_timer);

ts->tick_stopped = 1;

ts->idle_jiffies = last_jiffies;

rcu_enter_nohz();

}

ts->idle_sleeps++;

/* Mark expires */

ts->idle_expires = expires;

/*

* If the expiration time == KTIME_MAX, then

* in this case we simply stop the tick timer.

*/

if (unlikely(expires.tv64 == KTIME_MAX)) {

if (ts->nohz_mode == NOHZ_MODE_HIGHRES)

hrtimer_cancel(&ts->sched_timer);

goto out;

}

if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {

hrtimer_start(&ts->sched_timer, expires,

HRTIMER_MODE_ABS_PINNED);

/* Check, if the timer was already in the past */

if (hrtimer_active(&ts->sched_timer))

goto out;

} else if (!tick_program_event(expires, 0))

goto out;

/*

* We are past the event already. So we crossed a

* jiffie boundary. Update jiffies and raise the

* softirq.

*/

tick_do_update_jiffies64(ktime_get());

}

raise_softirq_irqoff(TIMER_SOFTIRQ);

out:

ts->next_jiffies = next_jiffies;

ts->last_jiffies = last_jiffies;

ts->sleep_length = ktime_sub(dev->next_event, now);

end:

local_irq_restore(flags);

}

----------------

tick_nohz_restart_sched_tick

tick_do_update_jiffies64

/* Account idle time */

/* Set tick_sched->tick_stopped = 0 */

/* program the next tick event */


/**

* tick_nohz_restart_sched_tick - restart the idle tick from the idle task

*

* Restart the idle tick when the CPU is woken up from idle

*/

void tick_nohz_restart_sched_tick(void)

{

int cpu = smp_processor_id();

struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);

#ifndef CONFIG_VIRT_CPU_ACCOUNTING

unsigned long ticks;

#endif

ktime_t now;

local_irq_disable();

if (ts->idle_active || (ts->inidle && ts->tick_stopped))

now = ktime_get();

if (ts->idle_active)

tick_nohz_stop_idle(cpu, now);

if (!ts->inidle || !ts->tick_stopped) {

ts->inidle = 0;

local_irq_enable();

return;

}

ts->inidle = 0;

rcu_exit_nohz();

/* Update jiffies first */

select_nohz_load_balancer(0);

tick_do_update_jiffies64(now);

#ifndef CONFIG_VIRT_CPU_ACCOUNTING

/*

* We stopped the tick in idle. Update process times would miss the

* time we slept as update_process_times does only a 1 tick

* accounting. Enforce that this is accounted to idle !

*/

ticks = jiffies - ts->idle_jiffies;

/*

* We might be one off. Do not randomly account a huge number of ticks!

*/

if (ticks && ticks < LONG_MAX)

account_idle_ticks(ticks);

#endif

touch_softlockup_watchdog();

/*

* Cancel the scheduled timer and restore the tick

*/

ts->tick_stopped = 0;

ts->idle_exittime = now;

tick_nohz_restart(ts, now);

local_irq_enable();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: