[PATCH - 2.0.30] RTC profiling as a config option.

Gordon Oliver (gordo@telsur.cl)
Mon, 11 Aug 1997 18:21:04 -0400


> THIS IS A MESSAGE IN 'MIME' FORMAT. Your mail reader does not support MIME.
> Some parts of this will be readable as plain text.
> To see the rest, you will need to upgrade your mail reader.

--PART.BOUNDARY.406.675.gringo.871338063.1
Content-ID: <406_675_871338063_2@gringo>
Content-type: text/plain

Hi all...
This was requested by at least one person on the list... so here it is.
This patch should be safe for pretty much anybody to install, but it has
the ugliness that sched.c now has to include config.h (ouch).
It shouldn't be too hard to port this up to 2.1.x, as it is fairly self
contained.
Should I send this to Linus to ask that it be included (reply to me
directly please) in the mainline kernel? Doesn't seem likely that it would
make it into the 2.0.31 kernel anyway.

NOTES:
- when CONFIG_RTC_PROFILE is set the real time clock is reset to 64 Hz.
in order to change this you have to go and edit a file (rtc.c)
- sched.c now does not profile if CONFIG_RTC_PROFILE is set.
- when CONFIG_RTC_PROFILE is set the periodic interrupt is enabled by
default, and will not be disabled on close. But the ioctl still
does disable it. If a piece of software disables the periodic
interrupt, it will disable profiling as well.
(This can be fixed, but...)

... here it is ...

--PART.BOUNDARY.406.675.gringo.871338063.1
Content-ID: <406_675_871338063_3@gringo>
Content-type: application/x-metamail-patch
Content-Transfer-Encoding: quoted-printable

diff --recursive -u -X patch-excludes linux-2.0.30pre3/Documentation/Conf=
igure.help linux/Documentation/Configure.help
--- linux-2.0.30pre3/Documentation/Configure.help Tue Aug 5 19:21:46 199=
7
+++ linux/Documentation/Configure.help Fri Aug 8 11:34:00 1997
@@ -3791,6 +3791,18 @@
enabled "Kernel profiling support", you must be a kernel hacker and
hence you know what this is about :-)
=

+Real time clock profiling
+CONFIG_RTC_PROFILE
+ If you enabled kernel profiling support, and the enhanced real time
+ clock support, this option will make the kernel profile from the rtc
+ interrupt. This interrupt is independent of kernel processing and
+ should give better results. By default this changes the clock rate
+ to 64 interrupts per second. (edit drivers/char/rtc.c to change the
+ initial rate, search for INIT_FREQ - there is a detailed comment in
+ the code.)
+ If you are using the real time clock interrupt (or /dev/rtc) for
+ something else, or are not sure, Say N.
+
ISDN subsystem
CONFIG_ISDN
ISDN ("Integrated Services Digital Networks", called RNIS in
diff --recursive -u -X patch-excludes linux-2.0.30pre3/arch/alpha/config.=
in linux/arch/alpha/config.in
--- linux-2.0.30pre3/arch/alpha/config.in Mon Aug 5 03:13:50 1996
+++ linux/arch/alpha/config.in Fri Aug 8 09:42:19 1997
@@ -161,5 +161,8 @@
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" =3D "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+ if [ "$CONFIG_RTC" =3D "y" ]; then
+ bool ' Real Time Clock profiling' CONFIG_RTC_PROFILE
+ fi
fi
endmenu
diff --recursive -u -X patch-excludes linux-2.0.30pre3/arch/i386/config.i=
n linux/arch/i386/config.in
--- linux-2.0.30pre3/arch/i386/config.in Mon May 13 00:17:23 1996
+++ linux/arch/i386/config.in Fri Aug 8 09:42:29 1997
@@ -110,5 +110,8 @@
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" =3D "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+ if [ "$CONFIG_RTC" =3D "y" ]; then
+ bool ' Real Time Clock profiling' CONFIG_RTC_PROFILE
+ fi
fi
endmenu
diff --recursive -u -X patch-excludes linux-2.0.30pre3/arch/m68k/config.i=
n linux/arch/m68k/config.in
--- linux-2.0.30pre3/arch/m68k/config.in Mon May 20 00:54:26 1996
+++ linux/arch/m68k/config.in Fri Aug 8 09:42:33 1997
@@ -178,5 +178,8 @@
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" =3D "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+ if [ "$CONFIG_RTC" =3D "y" ]; then
+ bool ' Real Time Clock profiling' CONFIG_RTC_PROFILE
+ fi
fi
endmenu
diff --recursive -u -X patch-excludes linux-2.0.30pre3/arch/mips/config.i=
n linux/arch/mips/config.in
--- linux-2.0.30pre3/arch/mips/config.in Sun May 5 02:05:58 1996
+++ linux/arch/mips/config.in Fri Aug 8 09:42:37 1997
@@ -114,5 +114,8 @@
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" =3D "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+ if [ "$CONFIG_RTC" =3D "y" ]; then
+ bool ' Real Time Clock profiling' CONFIG_RTC_PROFILE
+ fi
fi
endmenu
diff --recursive -u -X patch-excludes linux-2.0.30pre3/arch/ppc/config.in=
linux/arch/ppc/config.in
--- linux-2.0.30pre3/arch/ppc/config.in Mon May 27 05:00:57 1996
+++ linux/arch/ppc/config.in Fri Aug 8 09:42:42 1997
@@ -103,5 +103,8 @@
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" =3D "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+ if [ "$CONFIG_RTC" =3D "y" ]; then
+ bool ' Real Time Clock profiling' CONFIG_RTC_PROFILE
+ fi
fi
endmenu
diff --recursive -u -X patch-excludes linux-2.0.30pre3/arch/sparc/config.=
in linux/arch/sparc/config.in
--- linux-2.0.30pre3/arch/sparc/config.in Thu Apr 25 06:22:05 1996
+++ linux/arch/sparc/config.in Fri Aug 8 09:43:03 1997
@@ -108,4 +108,7 @@
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" =3D "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+ if [ "$CONFIG_RTC" =3D "y" ]; then
+ bool ' Real Time Clock profiling' CONFIG_RTC_PROFILE
+ fi
fi
diff --recursive -u -X patch-excludes linux-2.0.30pre3/drivers/char/rtc.c=
linux/drivers/char/rtc.c
--- linux-2.0.30pre3/drivers/char/rtc.c Tue May 28 00:39:18 1996
+++ linux/drivers/char/rtc.c Sat Aug 9 09:40:08 1997
@@ -50,6 +50,7 @@
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/mc146818rtc.h>
+#include <linux/config.h>
=

#include <asm/io.h>
#include <asm/segment.h>
@@ -62,6 +63,61 @@
=

#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.
+ * This is now a config option
+#define CONFIG_RTC_PROFILE 1
+ */
+
+#define RTC_IE_SET (RTC_PIE|RTC_AIE|RTC_UIE)
+
+#ifdef CONFIG_RTC_PROFILE
+
+#define INTERRUPT_FLAGS 0
+#define SAFETY cli()
+#define ENDSAFETY sti()
+#define RTC_IE_BASE (RTC_PIE)
+#define RTC_IE_CLEAR (RTC_AIE|RTC_UIE)
+#define INIT_TIMER set_rtc_timer()
+#define RELEASE_TIMER (void)NULL
+
+/*
+ * Init frequency is used to initialize the CMOS inturrupt selector
+ * to set the speed of the profiling interrupt. It is set to 10 here,
+ * which should be 64/sec. This is to slow the interrupts to a manageabl=
e
+ * rate, since the interrupts are heavier weight when profiling.
+ * The possible values are:
+ * 3: 8192 interrupts/sec
+ * 4: 4096 interrupts/sec
+ * 5: 2048 interrupts/sec
+ * 6: 1024 interrupts/sec
+ * 7: 512 interrupts/sec
+ * 8: 256 interrupts/sec
+ * 9: 128 interrupts/sec
+ * 10: 64 interrupts/sec
+ * 11: 32 interrupts/sec
+ * 12: 16 interrupts/sec
+ * 13: 8 interrupts/sec
+ * 14: 4 interrupts/sec
+ * 15: 2 interrupts/sec
+ */
+#define INIT_FREQ 10
+
+#else /* !CONFIG_RTC_PROFILE */
+
+#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
+#define INIT_TIMER (void)NULL
+#define RELEASE_TIMER clear_rtc_timer()
+#define INIT_FREQ 6
+
+#endif /* !CONFIG_RTC_PROFILE */
+
static struct wait_queue *rtc_wait;
=

static struct timer_list rtc_irq_timer;
@@ -108,6 +164,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 |=3D RTC_TIMER_ON;
+ rtc_irq_timer.expires =3D 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 &=3D ~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 =3D 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 +201,24 @@
=

rtc_irq_data +=3D 0x100;
rtc_irq_data &=3D ~0xff;
+ SAFETY;
rtc_irq_data |=3D (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 =3D jiffies + HZ/rtc_freq + 2*HZ/100;
- add_timer(&rtc_irq_timer);
+ reset_rtc_timer();
+#ifdef CONFIG_RTC_PROFILE
+ if (!regs)
+ return;
+ if (prof_buffer && current->pid && !user_mode(regs)) {
+ extern int _stext;
+ unsigned long ip =3D instruction_pointer(regs);
+ ip -=3D (unsigned long) &_stext;
+ ip >>=3D prof_shift;
+ if (ip < prof_len)
+ prof_buffer[ip]++;
}
+#endif /* CONFIG_RTC_PROFILE */
}
=

/*
@@ -204,10 +296,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 &=3D ~RTC_TIMER_ON;
- }
+ clear_rtc_timer();
return 0;
}
case RTC_PIE_ON: /* Allow periodic ints */
@@ -217,14 +306,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 |=3D RTC_TIMER_ON;
- rtc_irq_timer.expires =3D 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 +480,14 @@
case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
{
int retval;
+ unsigned int val;
=

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

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

- rtc_freq =3D arg;
+ rtc_freq =3D tmp;
=

save_flags(flags);
cli();
@@ -475,18 +562,11 @@
save_flags(flags);
cli();
tmp =3D CMOS_READ(RTC_CONTROL);
- tmp &=3D ~RTC_PIE;
- tmp &=3D ~RTC_AIE;
- tmp &=3D ~RTC_UIE;
+ tmp &=3D ~RTC_IE_CLEAR;
CMOS_WRITE(tmp, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
restore_flags(flags);
-
- if (rtc_status & RTC_TIMER_ON) {
- rtc_status &=3D ~RTC_TIMER_ON;
- del_timer(&rtc_irq_timer);
- }
-
+ RELEASE_TIMER;
rtc_irq_data =3D 0;
rtc_status &=3D ~RTC_IS_OPEN;
}
@@ -528,9 +608,13 @@
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))
+ printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
+#ifdef CONFIG_RTC_PROFILE
+ printk(KERN_INFO "Real Time Clock being used for profiling at %d Hz\n",=
1 << (16 - INIT_FREQ));
+#endif /* CONFIG_RTC_PROFILE */
+ 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);
@@ -545,9 +629,15 @@
save_flags(flags);
cli();
/* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
- CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELEC=
T);
+ CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | INIT_FREQ), RTC_FREQ_=
SELECT);
+ tmp =3D CMOS_READ(RTC_CONTROL);
+ tmp &=3D ~RTC_IE_SET;
+ tmp |=3D RTC_IE_BASE;
+ CMOS_WRITE(tmp, RTC_CONTROL);
+ CMOS_READ(RTC_INTR_FLAGS);
restore_flags(flags);
- rtc_freq =3D 1024;
+ rtc_freq =3D 16-INIT_FREQ;
+ INIT_TIMER;
return 0;
}
=

@@ -567,14 +657,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 =3D 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 +=3D ((rtc_freq/HZ)<<8);
+ rtc_irq_data +=3D (((1<<rtc_freq)/HZ)<<8);
rtc_irq_data &=3D ~0xff;
rtc_irq_data |=3D (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */
restore_flags(flags);
@@ -642,7 +730,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 +739,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 -X patch-excludes linux-2.0.30pre3/kernel/sched.c lin=
ux/kernel/sched.c
--- linux-2.0.30pre3/kernel/sched.c Tue Aug 5 19:21:59 1997
+++ linux/kernel/sched.c Fri Aug 8 09:59:26 1997
@@ -39,6 +39,7 @@
#include <asm/mmu_context.h>
=

#include <linux/timex.h>
+#include <linux/config.h>
=

/*
* kernel variables
@@ -1230,6 +1231,7 @@
mark_bh(TIMER_BH);
if (!user_mode(regs)) {
lost_ticks_system++;
+#ifndef CONFIG_RTC_PROFILE
if (prof_buffer && current->pid) {
extern int _stext;
unsigned long ip =3D instruction_pointer(regs);
@@ -1238,6 +1240,7 @@
if (ip < prof_len)
prof_buffer[ip]++;
}
+#endif
}
if (tq_timer)
mark_bh(TQUEUE_BH);

--PART.BOUNDARY.406.675.gringo.871338063.1--