[2.0.x] RTC Profiling patch.

Gordon Oliver (gordo@telsur.cl)
Wed, 6 Aug 1997 09:20:46 -0400 (CST)


Hi all...

This is a patch to do kernel profiling from the real time clock
driver. I had it as a config option at one point, but I killed that
in porting it to a new kernel... I doubt this should go in the main
line kernel anyway. This patch is against stock 2.0.30, but applies
cleanly to 2.0.30+pre-patch-3. It will probably apply against 2.1.x
with minimal work.

What it does:
- disables the profiling code in the timer interupt (sched.c)
- changes the default rtc rate to 128 interrupts per second on boot
- maintains the interrupt even when no one is asking for it.
- profiles within the interrupt (had to change it to a heavyweight
interrupt to do this :-(, because the fast interrupts don't save
enough registers.

Why use it:
The profiling information has less dependency on the system, since
the timer interrupt triggers all kinds of activity it is not a very
good place to actually profile from. This patch should make the
profiling information more accurate.

Why not use it:
Because you use the RTC for something else (no guarantees on
interoperability, though it should work)
Because you don't profile.

----------------------------------------------------------------------------
diff --recursive -u --new-file linux-2.0.30/drivers/char/rtc.c linux/drivers/char/rtc.c
--- linux-2.0.30/drivers/char/rtc.c Tue May 28 00:39:18 1996
+++ linux/drivers/char/rtc.c Thu Apr 10 12:48:26 1997
@@ -62,6 +62,33 @@

#define RTC_MINOR 135

+/*
+ * CONFIG_PROFILE_RTC turns on the RTC interrupt as the profiler.
+ * in this case, the rtc device is not entirely useful...
+ * However, the profiling data gathered will be much better.
+ */
+#define CONFIG_PROFILE_RTC 1
+
+#define RTC_IE_SET (RTC_PIE|RTC_AIE|RTC_UIE)
+
+#ifdef CONFIG_PROFILE_RTC
+
+#define INTERRUPT_FLAGS 0
+#define SAFETY cli()
+#define ENDSAFETY sti()
+#define RTC_IE_BASE (RTC_PIE)
+#define RTC_IE_CLEAR (RTC_AIE|RTC_UIE)
+
+#else /* !CONFIG_PROFILE_RTC */
+
+#define INTERRUPT_FLAGS SA_INTERRUPT
+#define SAFETY (void)NULL
+#define ENDSAFETY (void)NULL
+#define RTC_IE_BASE (0)
+#define RTC_IE_CLEAR RTC_IE_SET
+
+#endif /* !CONFIG_PROFILE_RTC */
+
static struct wait_queue *rtc_wait;

static struct timer_list rtc_irq_timer;
@@ -108,6 +135,32 @@
* (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
*/

+static inline void set_rtc_timer(void)
+{
+ if (!(rtc_status & RTC_TIMER_ON)) {
+ rtc_status |= RTC_TIMER_ON;
+ rtc_irq_timer.expires = jiffies + (HZ>>rtc_freq) + 2*HZ/100;
+ add_timer(&rtc_irq_timer);
+ }
+}
+
+static inline void clear_rtc_timer(void)
+{
+ if (!(rtc_status & RTC_TIMER_ON)) {
+ del_timer(&rtc_irq_timer);
+ rtc_status &= ~RTC_TIMER_ON;
+ }
+}
+
+static inline void reset_rtc_timer(void)
+{
+ if (rtc_status & RTC_TIMER_ON) {
+ del_timer(&rtc_irq_timer);
+ rtc_irq_timer.expires = jiffies + (HZ>>rtc_freq) + 2*HZ/100;
+ add_timer(&rtc_irq_timer);
+ }
+}
+
static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
/*
@@ -119,14 +172,24 @@

rtc_irq_data += 0x100;
rtc_irq_data &= ~0xff;
+ SAFETY;
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
+ ENDSAFETY;
wake_up_interruptible(&rtc_wait);

- if (rtc_status & RTC_TIMER_ON) {
- del_timer(&rtc_irq_timer);
- rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
- add_timer(&rtc_irq_timer);
+ reset_rtc_timer();
+#ifdef CONFIG_PROFILE_RTC
+ if (!regs)
+ return;
+ if (prof_buffer && current->pid && !user_mode(regs)) {
+ extern int _stext;
+ unsigned long ip = instruction_pointer(regs);
+ ip -= (unsigned long) &_stext;
+ ip >>= prof_shift;
+ if (ip < prof_len)
+ prof_buffer[ip]++;
}
+#endif /* CONFIG_PROFILE_RTC */
}

/*
@@ -204,10 +267,7 @@
case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
{
mask_rtc_irq_bit(RTC_PIE);
- if (rtc_status & RTC_TIMER_ON) {
- del_timer(&rtc_irq_timer);
- rtc_status &= ~RTC_TIMER_ON;
- }
+ clear_rtc_timer();
return 0;
}
case RTC_PIE_ON: /* Allow periodic ints */
@@ -217,14 +277,10 @@
* We don't really want Joe User enabling more
* than 64Hz of interrupts on a multi-user machine.
*/
- if ((rtc_freq > 64) && (!suser()))
+ if ((rtc_freq > 6) && (!suser()))
return -EACCES;

- if (!(rtc_status & RTC_TIMER_ON)) {
- rtc_status |= RTC_TIMER_ON;
- rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
- add_timer(&rtc_irq_timer);
- }
+ set_rtc_timer();
set_rtc_irq_bit(RTC_PIE);
return 0;
}
@@ -395,12 +451,14 @@
case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
{
int retval;
+ unsigned int val;

retval = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long));
if (retval != 0)
return retval;

- memcpy_tofs((unsigned long*)arg, &rtc_freq, sizeof(unsigned long));
+ val = 1<<rtc_freq;
+ memcpy_tofs((unsigned long*)arg, &val, sizeof(unsigned long));
return 0;
}
case RTC_IRQP_SET: /* Set periodic IRQ rate. */
@@ -429,7 +487,7 @@
if (arg != (1<<tmp))
return -EINVAL;

- rtc_freq = arg;
+ rtc_freq = tmp;

save_flags(flags);
cli();
@@ -475,17 +533,14 @@
save_flags(flags);
cli();
tmp = CMOS_READ(RTC_CONTROL);
- tmp &= ~RTC_PIE;
- tmp &= ~RTC_AIE;
- tmp &= ~RTC_UIE;
+ tmp &= ~RTC_IE_CLEAR;
CMOS_WRITE(tmp, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
restore_flags(flags);

- if (rtc_status & RTC_TIMER_ON) {
- rtc_status &= ~RTC_TIMER_ON;
- del_timer(&rtc_irq_timer);
- }
+#ifndef CONFIG_PROFILE_RTC
+ clear_rtc_timer();
+#endif /* CONFIG_PROFILE_RTC */

rtc_irq_data = 0;
rtc_status &= ~RTC_IS_OPEN;
@@ -528,9 +583,10 @@
int rtc_init(void)
{
unsigned long flags;
+ unsigned char tmp;

printk("Real Time Clock Driver v%s\n", RTC_VERSION);
- if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
+ if(request_irq(RTC_IRQ, rtc_interrupt, 0 /*SA_INTERRUPT*/, "rtc", NULL))
{
/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
printk("rtc: IRQ %d is not free.\n", RTC_IRQ);
@@ -546,8 +602,16 @@
cli();
/* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
+ tmp = CMOS_READ(RTC_CONTROL);
+ tmp &= ~RTC_IE_SET;
+ tmp |= RTC_IE_BASE;
+ CMOS_WRITE(tmp, RTC_CONTROL);
+ CMOS_READ(RTC_INTR_FLAGS);
restore_flags(flags);
- rtc_freq = 1024;
+ rtc_freq = 10;
+#ifdef CONFIG_PROFILE_RTC
+ set_rtc_timer();
+#endif /* CONFIG_PROFILE_RTC */
return 0;
}

@@ -567,14 +631,12 @@
{
unsigned long flags;

- printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq);
- del_timer(&rtc_irq_timer);
- rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
- add_timer(&rtc_irq_timer);
+ printk(KERN_INFO "rtc: lost some interrupts at %dHz.\n", 1<<rtc_freq);
+ reset_rtc_timer();

save_flags(flags);
cli();
- rtc_irq_data += ((rtc_freq/HZ)<<8);
+ rtc_irq_data += (((1<<rtc_freq)/HZ)<<8);
rtc_irq_data &= ~0xff;
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */
restore_flags(flags);
@@ -642,7 +704,7 @@
"alarm_IRQ\t: %s\n"
"update_IRQ\t: %s\n"
"periodic_IRQ\t: %s\n"
- "periodic_freq\t: %ld\n"
+ "periodic_freq\t: %d\n"
"batt_status\t: %s\n",
(ctrl & RTC_DST_EN) ? "yes" : "no",
(ctrl & RTC_DM_BINARY) ? "no" : "yes",
@@ -651,7 +713,7 @@
(ctrl & RTC_AIE) ? "yes" : "no",
(ctrl & RTC_UIE) ? "yes" : "no",
(ctrl & RTC_PIE) ? "yes" : "no",
- rtc_freq,
+ 1<<rtc_freq,
batt ? "okay" : "dead");

return p - buf;
diff --recursive -u --new-file linux-2.0.30/kernel/sched.c linux/kernel/sched.c
--- linux-2.0.30/kernel/sched.c Thu Apr 10 12:19:43 1997
+++ linux/kernel/sched.c Thu Apr 10 12:58:31 1997
@@ -1230,6 +1230,7 @@
mark_bh(TIMER_BH);
if (!user_mode(regs)) {
lost_ticks_system++;
+#if 0
if (prof_buffer && current->pid) {
extern int _stext;
unsigned long ip = instruction_pointer(regs);
@@ -1238,6 +1239,7 @@
if (ip < prof_len)
prof_buffer[ip]++;
}
+#endif
}
if (tq_timer)
mark_bh(TQUEUE_BH);