Local APIC timer interrupt

The second-level handler for this increments the running count of timer ticks and acknowledges the irq. Then it calls a third-level handler, which does the sort of housekeeping work associated with timer interrupts.

13.7.3.1 Second-level timer handling

Each local APIC generates a timer interrupt. Figure 13.34, from arch/i386/kernel/apic.c, shows the second-level handler for this interrupt.

1021 unsigned int apic_timer_irqs [NR_CPUS];

1022

102 3 void smp_apic_timer_interrupt(struct pt_regs * regs)

1025 int cpu = smp_processor_id(); 1030 apic_timer_irqs[cpu]++; 1036 ack_APIC_irq();

1043 smp_local_timer_interrupt(regs);

1045

1046 if(softirq_pending(cpu))

1047 do_softirq();

Figure 13.34 Second-level handler for the APIC (advanced programmable interrupt controller) timer

1021 this array maintains a separate total of APIC timer interrupts for each CPU.

1025 this gets the ID of the currently executing CPU (see Section 7.2.1.4).

1030 this increments the count of APIC timer interrupts for this CPU. The nonmaskable interrupt deadlock detection uses this count (see Section 11.3.2.2).

1036 the irq is acknowledged at this stage, because timer handling (line 1043) can be slow. The acknowledging function was described in Section 13.3.2.4.

1042 this macro, and its companion on line 1044, keeps track of the number of (nested) interrupts currently in progress on this CPU. The macros are defined in Section 16.5.3. The second parameter is not used by either of the macros, so it is set to 0.

1043 this function handles work that needs to be done on every timer tick (see Section 13.7.3.2).

1046 the macro that checks if there are software interrupts waiting to be executed is from Section 16.5.1.2.

1047 software interrupts are run at least every timer tick, using the function from Section 16.1.3.

13.7.3.2 Third-level timer handling

The local timer interrupt handler, from arch/i386/kernel/apic. c, is shown in Figure 13.35. It does both profiling and process statistics/rescheduling. Profiling is done on every local tick, statistics/rescheduling happen only after a fixed number of ticks, although the default for this is 1. It can be changed by writing the new multiplier value into /proc/profile.

967 inline void smp_local_timer_interrupt(struct pt_regs * regs)

969 intuser = user_mode(regs);

979 x86_do_profile(regs->eip);

prof_counter[cpu]=prof_multiplier[cpu]; if (prof_counter[cpu] ! = prof_old_multiplier[cpu]){

setup_APIC_LVTT(calibration_result/prof_counter[cpu]);

prof_old_multiplier[cpu]=prof_counter[cpu];

996 #ifdefCONFIG_SMP

997 update_process_times(user);

998 #endif

Figure 13.35 The local timer interrupt handler

970 978-979

981-999

990 991-994

993 996-998

this macro evaluates to TRUE if the CPU was running in user mode, or in vm86 mode, when the interrupt occurred (see Section 10.3.1.4).

this gets the ID of the currently executing processor (see Section 7.2.1.4).

if the CPU was running in kernel mode when the timer ticked, then there is a special profiling function called (see Section 13.7.3.3).

this code is not executed on every interrupt but only at a frequency determined by the multiplier. The copy of the multiplier maintained in prof_counter[ cpu] is decremented each tick.

as the running counter has expired, it needs to be set up again.

the multiplier may have changed since the last time this line was executed, as a result of the user writing to /proc/profile. This user intervention changes the value in the appropriate element of prof_multiplier[]; it does not change the corresponding element in prof_old_multiplier[]. The APIC timer needs to know this and has to be adjusted accordingly.

the function is from Section 13.7.3.4. Because the multiplier (in prof_counter[cpu]) has changed, the APIC timer has to be recalibrated.

now that the hardware has been recalibrated, the old multiplier is not relevant and that field is updated.

in a multiprocessor system, process times are updated at this less frequent interval as well (see Section 15.1.3.1). Otherwise, on a uniprocessor system, this function is called from do_timer() (see Section 15.1.2).

Profiling function

The function that does profiling in Linux is shown in 13.36, from <asm-i386/hw_irq.h>. It is called only when a timer tick occurs in kernel mode, and it profiles only kernel usage.

192 static inline void x86_do_profile (unsigned long eip)

194 if(!prof_buffer)

return;

201 202

if (!((1<<smp_processor_id()) &prof_cpu_mask)) return;

eip -= (unsigned long) &_stext; eip >>= prof_shift;

211 212 213

eip = prof_len-1; atomic_inc((atomic_t *)&prof_buffer[eip]);

Figure 13.36 Profiling kernel usage

192 the function is passed a pointer to the saved value of EIP on the stack (i.e. a pointer to where it was in kernel code when the interrupt occurred).

194-195 if a buffer for profiling information has not been set up by start_kernel(), we return.

201-202 if the current CPU is not set up to be profiled, we return.

204 subtracting the address of _stext (the beginning of the kernel code segment) from the EIP value converts an absolute address to an offset into kernel code.

205 this determines the granularity of the profiling, by taking only high-order bits of the offset into account.

211-212 if the resultant value is out of bounds, it is converted so that it belongs in the last entry in the table. In this way, such values will stand out.

213 this increments the appropriate entry in the profiling table.

13.7.3.4 Setting up the local APIC timer

The function shown in Figure 13.37, from arch/i386/kernel/apic. c, sets up the local APIC timer. This is done at initialisation but may also be called if the user changes the multiplier value in /proc/profile.

761 void_setup_APIC_LVTT(unsigned int clocks)

763 unsigned int lvtt1_value, tmp_value;

76 5 lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV)

766 | APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;

767 apic_write_around(APIC_LVTT,lvtt1_value);

772 tmp_value = apic_read(APIC_TDCR);

773 apic_write_around(APIC_TDCR,(tmp_value

777 apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);

Figure 13.37 Setting up the local APIC (advanced programmable interrupt controller) timer

761 the parameter is the interval at which the timer is to interrupt.

765-766 the SET_APIC_TIMER_BASE() macro sets up the timer base field (bits 18-19) with APIC_TIMER_BASE_DIV (10 binary). APIC_LVT_TIMER_PERIODIC is bit 17, the timer mode field. This code is setting it to be periodic. These three macros are from Section 13.1.4. LOCAL_TIMER_VECTOR is defined in Section 12.5.3. This is the least significant 8 bits, so the whole thing builds up a value for the local timer register in lvtt 1_value.

767 this value is written to the APIC_LVTT register, using the macro from Section 13.4.2.

772 this reads the current value in the timer divide configuration register (APIC_TDCR).

773-775 this clears the APIC_TDR_DIV_1 (bits 0, 1 and 4) and APIC_TDR_DIV_TMBASE (bit 2) and sets APIC_TDR_DIV_16 (bits 0 and 1); it then writes all this back to the timer divide configuration register.

777 the APIC_TMICT, the initial count register field of the timer, is set up by using the function described in Section 13.4.2. The input parameter is divided by the value from arch/i386/kernel/apic.c:

759 #define APIC_DIVISOR 16

Was this article helpful?

0 0

Post a comment