[patch][x86][hardcntr] Harware counter per process support

David Mentre (David.Mentre@irisa.fr)
26 Nov 1997 16:10:44 +0100


--Multipart_Wed_Nov_26_16:10:43_1997-1
Content-Type: text/plain; charset=US-ASCII

Hi all linux hackers,

<resume-for-linus> ;)

Per task/process hardware counters support for PPro and
PII. /proc/<pid> interface.

</resume-for-linus>

Various recent processors (PPro, PII, Alpha, MIPS R10000, PPC 604e)
have hardware counters builtin to count various events (cache misses,
instruction fetch, etc.). I have made a patch (SilentCounters(tm) :) to
provide access to such counters in a per process manner (patch which can
be applied against 2.1.65 and 2.1.66).

This patch add /proc/<pid>/hardcntr directory with ctrl0 and ctrl1 to
access Pentium (PPro and PII) control registers and cntr0 and cntr1 to
access counter registers.

With this patch, it is now possible to monitor per process behaviour
without any impact on the process.

Actually, a per process copy of hardware counters is maintained for
each task and hardware counters are changed at each context switch.

Of course, this patch is far from perfect, therefore I would like to
ask mainly two things:

1. For Linux gurus (Linus, Alan Cox, David Miller, Bill Hawes, Ingo
Molnar, ...): is it The Right Way(tm) to do such a thing ?

2. For other people: could they try this patch on various x86 hardware
(classic pentium) and report success or failure ? I am also interested
in AMD and Cyrix users to help me to support those architectures.

I have made a page (http://www.irisa.fr/prive/mentre/linux-counters/)
to provide various information about hardware counters. I would be glad
to have further information to complete it.

Features:
- SMP support
- updates at each context switch
- works on PII (tested), should work on PPro (untested)

Future improvements planned:
- Cyrix and AMD support (anybody with such hardware to help me ?)
- multi-architecture support (anybody with an Alpha to help me as I
haven't any access to a Linux Alpha machine ?)
- userland apps to use those counters.
- Hardware-Counters-HOWTO
- ??

d.

-- 
 David.Mentre@irisa.fr -- CAPS research team - Irisa - France
      Personal info: http://www.irisa.fr/prive/mentre/	
  Professional info: http://www.irisa.fr/caps/PEOPLE/David/

--Multipart_Wed_Nov_26_16:10:43_1997-1 Content-Type: application/octet-stream; type=patch Content-Disposition: attachment; filename="hardcntr-2.patch" Content-Transfer-Encoding: quoted-printable

diff -u -N -r linux-2.1.65-clean/arch/i386/config.in linux-2.1.65-hardcnt= r/arch/i386/config.in --- linux-2.1.65-clean/arch/i386/config.in Thu Oct 30 00:34:54 1997 +++ linux-2.1.65-hardcntr/arch/i386/config.in Fri Nov 21 20:01:42 1997 @@ -32,6 +32,9 @@ fi fi bool 'MCA support' CONFIG_MCA +if [ "$CONFIG_PROC_FS" =3D "y" ]; then + bool 'Hardware counters support' CONFIG_HARDCNTR +fi bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT diff -u -N -r linux-2.1.65-clean/arch/i386/kernel/Makefile linux-2.1.65-h= ardcntr/arch/i386/kernel/Makefile --- linux-2.1.65-clean/arch/i386/kernel/Makefile Wed May 14 07:41:00 1997=

+++ linux-2.1.65-hardcntr/arch/i386/kernel/Makefile Fri Nov 21 20:06:23 1= 997 @@ -26,6 +26,10 @@ O_OBJS +=3D mca.o endif =

+ifdef CONFIG_HARDCNTR +O_OBJS +=3D hardcntr.o +endif + ifdef SMP =

O_OBJS +=3D smp.o trampoline.o diff -u -N -r linux-2.1.65-clean/arch/i386/kernel/hardcntr.c linux-2.1.65= -hardcntr/arch/i386/kernel/hardcntr.c --- linux-2.1.65-clean/arch/i386/kernel/hardcntr.c Thu Jan 1 01:00:00 19= 70 +++ linux-2.1.65-hardcntr/arch/i386/kernel/hardcntr.c Mon Nov 24 17:24:56= 1997 @@ -0,0 +1,424 @@ +/* + * linux/arch/i386/kernel/hardcntr.c (aka SilentCounters :) + * + * Copyright (C) 1997 David Mentr\'e <David.Mentre@irisa.fr> + * + * This file is (heavily) inspired from MSR patch made by Stephan Meyer + * <Stephan.Meyer@pobox.com> + * + * Also inspired from linux/fs/proc/fd.c, linux/fs/proc/root.c and + * linux/fs/proc/array.c. In fact, we can say that is real mix. :) + * + */ + +/* =

+ * To do: + * - reduce overhead by optimizing read/write_msr routines + * - do we need to copy hard counters status at fork()/clone() time ? + * Maybe increment father counters from child counters if same event. + * - support for other architectures + * - sysctl ?? + * =

+ * Problems: + * - /proc interface<-->hard counters updated only on context switch + */ + +/* + * Simple doc: =

+ * + * echo 00430043 >! ctrl0 #setup counter control 0 to count event 0x43 + * #(data_mem_refs) + * cat cntr0 # the number of events counted + * =

+ */ + +#include <asm/uaccess.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> + +#include <linux/init.h> +#include <linux/string.h> +#include <asm/processor.h> + +unsigned int have_hardcntr =3D 0; + +static int proc_lookup_hardcntr_dir(struct inode *, struct dentry *); +static int proc_read_hardcntr_dir(struct file *, void *, filldir_t); +ssize_t proc_hardcntr_read_file (struct file *, char *, size_t, loff_t *= ); +ssize_t proc_hardcntr_write_file (struct file *, const char *, size_t, l= off_t *); + +char *hardcntr_dir_content[] =3D { + "ctrl0", + "ctrl1", + "cntr0", + "cntr1" +}; + +static struct file_operations proc_hardcntr_dir_op =3D { + NULL, /* lseek - default */ + NULL, /* read - bad */ + NULL, /* write - bad */ + proc_read_hardcntr_dir, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +/* + * proc directories can do almost nothing.. + */ +struct inode_operations proc_hardcntr_dir_inode_op =3D { + &proc_hardcntr_dir_op, /* default base directory file-ops */ + NULL, /* create */ + proc_lookup_hardcntr_dir,/* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +/* proc_hardcntr file operations */ +struct file_operations proc_hardcntr_file_op =3D { + NULL, /* llseek */ + proc_hardcntr_read_file, + proc_hardcntr_write_file, + NULL, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +struct inode_operations proc_hardcntr_file_inode_op =3D { + &proc_hardcntr_file_op, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +/* Read the machine specific register. + Heavily inspired from Stephan Meyer msr_value_read_proc routine */ =

+void __inline__ read_msr(unsigned long msr_index, unsigned long *hi, uns= igned long *lo) +{ + __asm__ __volatile__ ( + "rdmsr" + :"=3Da" (*lo), "=3Dd" (*hi) + :"c" (msr_index) + :"eax","ecx","edx"); =

+} + +/* Write the machine specific register. + Heavily inspired from Stephan Meyer msr_value_write_proc routine */ =

+void __inline__ write_msr(unsigned long msr_index, unsigned long hi, uns= igned long lo) +{ + __asm__ __volatile__ ( + "wrmsr" + : + :"a" (lo),"c" (msr_index),"d" (hi) + :"eax","ecx","edx"); =

+} + +#define PERFCTR0 0xC1 +#define PERFCTR1 0xC2 +#define EVNTSEL0 0x186 +#define EVNTSEL1 0x187 + +void save_hardcntr_to_task(struct task_struct *p) +{ + read_msr(PERFCTR0, &p->tss.hardcntr.cntr0.hi, &p->tss.hardcntr.c= ntr0.lo); + read_msr(PERFCTR1, &p->tss.hardcntr.cntr1.hi, &p->tss.hardcntr.c= ntr1.lo); +} + +void set_hardcntr_from_task(struct task_struct *p) +{ + write_msr(PERFCTR0, p->tss.hardcntr.cntr0.hi, p->tss.hardcntr.cn= tr0.lo); + write_msr(PERFCTR1, p->tss.hardcntr.cntr1.hi, p->tss.hardcntr.cn= tr1.lo); + write_msr(EVNTSEL1, 0, p->tss.hardcntr.ctrl1); + write_msr(EVNTSEL0, 0, p->tss.hardcntr.ctrl0); /* start counters= */ +} + +/* Check for MSR support and ... return. + * Stolen from Stephan.Meyer@pobox.com + */ +__initfunc(long hardcntr_init(long memory_start, long memory_end)) +{ + unsigned long hi =3D 0, lo =3D 0; + =

+ /* processor with machine specific registers? */ + if ((!have_cpuid) || (!(x86_capability & 0x20))) { + printk("One of your CPU does not support the hardware co= unters!\n"); + return memory_start; + } + + have_hardcntr =3D 1; + printk("Hardware counters installed -- "); + /* Make a small test */ + write_msr(PERFCTR0, 0, 0); + write_msr(EVNTSEL0, 0, 0x004300C0); /* C0: Number of + instructions retired */ + read_msr(EVNTSEL0, &hi, &lo); =

+ printk("ctrl0=3D%.8lx ", lo); + read_msr(PERFCTR0, &hi, &lo); =

+ printk("(inst. retired)=3D%lu\n", lo); + write_msr(EVNTSEL0, 0, 0); + =

+ return memory_start; +} + + +void switch_hardcntr(struct task_struct *prev, struct task_struct *next)=

+{ + if (!have_hardcntr) + return; + =

+#ifdef __SMP__ + if (prev->processor !=3D next->processor) { + /* would it appeared ? */ + printk("switch_hardcntr: unable to switch, not the same = proc!\n"); + return; + } +#endif =

+ /* save prev task counters */ + if (prev->tss.hardcntr.ctrl0) + save_hardcntr_to_task(prev); + write_msr(EVNTSEL0, 0, 0); /* Stop counters */ =

+ /* restore next task counters */ + if (next->tss.hardcntr.ctrl0) + set_hardcntr_from_task(next); +} + +/* Stolen from fs/proc/fd.c */ +static int proc_lookup_hardcntr_dir(struct inode * dir, struct dentry * = dentry) +{ + unsigned int ino, pid, i; + struct task_struct * p; + struct inode *inode; + =

+ if (!have_hardcntr) + return -ENOENT; + =

+ ino =3D dir->i_ino; + pid =3D ino >> 16; + ino &=3D 0x0000ffff; + if (!dir) + return -ENOENT; + if (!pid || ino !=3D PROC_PID_HARDCNTR || !S_ISDIR(dir->i_mode)) + return -ENOENT; + + for (i=3D0 ; i<4 ; i++) { + if (!strcmp(dentry->d_name.name, hardcntr_dir_content[i]= )) { + break; + } + } + if (i =3D=3D 4) + return -ENOENT; + =

+ p =3D find_task_by_pid(pid); + if (!pid || !p) + return -ENOENT; + =

+ ino =3D (pid << 16) + (PROC_PID_FD_DIR << 8) + i + 2; + =

+ inode =3D proc_get_inode(dir->i_sb, ino, NULL); + if (!inode) + return -ENOENT; + inode->i_op =3D &proc_hardcntr_file_inode_op; + inode->i_mode =3D S_IFREG | S_IRUSR | S_IWUSR; + =

+ d_add(dentry, inode); + return 0; +} + +/* Stolen from fs/proc/fd.c */ +static int proc_read_hardcntr_dir(struct file * filp, + void * dirent, filldir_t filldir) +{ + struct task_struct * p, **tarrayp; + unsigned int fd, pid, ino; + struct inode *inode =3D filp->f_dentry->d_inode; + =

+ if (!have_hardcntr) + return -ENOENT; + =

+ if (!inode || !S_ISDIR(inode->i_mode)) + return -EBADF; + ino =3D inode->i_ino; + pid =3D ino >> 16; + ino &=3D 0x0000ffff; + if (ino !=3D PROC_PID_HARDCNTR) + return 0; + + for (fd =3D filp->f_pos; fd < 2; fd++, filp->f_pos++) { + unsigned long ino =3D inode->i_ino; + if (fd) + ino =3D (ino & 0xffff0000) | PROC_PID_INO; + if (filldir(dirent, "..", fd+1, fd, ino) < 0) + return 0; + } + + p =3D find_task_by_pid(pid); + if(!p) + return -ENOENT; + tarrayp =3D p->tarray_ptr; + + while (filp->f_pos < 6) { + ino =3D (pid << 16) + (PROC_PID_HARDCNTR_DIR << 8) + fil= p->f_pos; + if (filldir(dirent, hardcntr_dir_content[filp->f_pos-2],=

+ sizeof(hardcntr_dir_content[filp->f_pos-2]) = + 1, + filp->f_pos, ino) < 0) + break; + + /* filldir() might have slept, so we must re-validate "p" */ + if (p !=3D *tarrayp || p->pid !=3D pid) + break; + filp->f_pos++; + } + return 0; +} + +#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines=

+ use some slack for overruns */ +#define BUF_SIZE 20 + +ssize_t proc_hardcntr_read_file(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + struct inode * inode =3D file->f_dentry->d_inode; + ssize_t length; + ssize_t end; + unsigned int type, pid; + struct task_struct * p; + char loc_buf[BUF_SIZE]; + =

+ type =3D inode->i_ino; + pid =3D type >> 16; + type &=3D 0x000000ff; + + p =3D find_task_by_pid(pid); + if (!p) + return -ENOENT; + + if (count > BUF_SIZE) + count =3D BUF_SIZE; + =

+ switch (type) { + case 2: /* ctrl0 */ + length =3D sprintf(loc_buf, "%.8lx", p->tss.hardcntr.ctr= l0); + break; + case 3: /* ctrl1 */ + length =3D sprintf(loc_buf, "%.8lx", p->tss.hardcntr.ctr= l1); + break; + case 4: /* cntr0 */ + length =3D sprintf(loc_buf, "%.8lx%.8lx", + p->tss.hardcntr.cntr0.hi, + p->tss.hardcntr.cntr0.lo); + break; + case 5: /* cntr1 */ + length =3D sprintf(loc_buf, "%.8lx%.8lx", + p->tss.hardcntr.cntr1.hi, + p->tss.hardcntr.cntr1.lo); + break; + default: + printk("proc_hardcntr_read_file: unknown type: %d\n", ty= pe); + return -ENOENT; + } + =

+ if (length < 0) + return length; + =

+ if (*ppos >=3D length) + return 0; + if (count + *ppos > length) + count =3D length - *ppos; + end =3D count + *ppos; + copy_to_user(buf, (char *) loc_buf + *ppos, count); + *ppos =3D end; + + return count; +} + +ssize_t proc_hardcntr_write_file(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct inode * inode =3D file->f_dentry->d_inode; + unsigned int type, pid; + struct task_struct * p; + char loc_buf[BUF_SIZE]; + char *e; + unsigned int val; + =

+ type =3D inode->i_ino; + pid =3D type >> 16; + type &=3D 0x000000ff; + + p =3D find_task_by_pid(pid); + if (!p) + return -ENOENT; + + if (count > BUF_SIZE) + return -EOVERFLOW; + + if (*ppos >=3D count) + return 0; + + copy_from_user(loc_buf, buf, count); + =

+ val =3D simple_strtoul(loc_buf, &e, 16); /* stolen from Stephan + Meyer */ + if (!e) return -EINVAL; + =

+ switch (type) { + case 2: /* ctrl0 */ + p->tss.hardcntr.ctrl0 =3D val; + if (!val) { + p->tss.hardcntr.cntr0.hi =3D 0; + p->tss.hardcntr.cntr0.lo =3D 0; + } + break; + case 3: /* ctrl1 */ + p->tss.hardcntr.ctrl1 =3D val; + if (!val) { + p->tss.hardcntr.cntr1.hi =3D 0; + p->tss.hardcntr.cntr1.lo =3D 0; + } + break; + case 4: /* cntr0 */ + case 5: /* cntr1 */ + return -EACCES; + default: + printk("proc_hardcntr_write_file: unknown type: %d\n", t= ype); + return -ENOENT; + } + =

+ *ppos =3D count + *ppos; + + return count; +} diff -u -N -r linux-2.1.65-clean/fs/proc/base.c linux-2.1.65-hardcntr/fs/= proc/base.c --- linux-2.1.65-clean/fs/proc/base.c Sun Oct 12 19:16:38 1997 +++ linux-2.1.65-hardcntr/fs/proc/base.c Mon Nov 24 17:23:13 1997 @@ -14,6 +14,10 @@ #include <linux/proc_fs.h> #include <linux/stat.h> =

+#if CONFIG_HARDCNTR +extern unsigned int have_hardcntr; +#endif + static struct file_operations proc_base_operations =3D { NULL, /* lseek - default */ NULL, /* read - bad */ @@ -118,6 +122,14 @@ 0, &proc_fd_inode_operations, NULL, proc_pid_fill_inode, }; +#if CONFIG_HARDCNTR +static struct proc_dir_entry proc_pid_hardcntr =3D { + PROC_PID_HARDCNTR, 8, "hardcntr", + S_IFDIR | S_IRUSR | S_IXUSR, 1, 0, 0, + 0, &proc_hardcntr_dir_inode_op, + NULL, proc_pid_fill_inode, +}; +#endif static struct proc_dir_entry proc_pid_environ =3D { PROC_PID_ENVIRON, 7, "environ", S_IFREG | S_IRUSR, 1, 0, 0, @@ -169,6 +181,10 @@ proc_register(&proc_pid, &proc_pid_root); proc_register(&proc_pid, &proc_pid_exe); proc_register(&proc_pid, &proc_pid_fd); +#if CONFIG_HARDCNTR + if (have_hardcntr) + proc_register(&proc_pid, &proc_pid_hardcntr); +#endif proc_register(&proc_pid, &proc_pid_environ); proc_register(&proc_pid, &proc_pid_cmdline); proc_register(&proc_pid, &proc_pid_stat); diff -u -N -r linux-2.1.65-clean/include/asm-i386/processor.h linux-2.1.6= 5-hardcntr/include/asm-i386/processor.h --- linux-2.1.65-clean/include/asm-i386/processor.h Wed May 14 07:41:17 1= 997 +++ linux-2.1.65-hardcntr/include/asm-i386/processor.h Mon Nov 24 12:04:0= 5 1997 @@ -87,6 +87,20 @@ struct i387_soft_struct soft; }; =

+#if CONFIG_HARDCNTR +struct counter_struct { + unsigned long hi; + unsigned long lo; +}; + +struct hardware_counters_struct { + unsigned long ctrl0; + unsigned long ctrl1; + struct counter_struct cntr0; + struct counter_struct cntr1; +}; +#endif + struct thread_struct { unsigned short back_link,__blh; unsigned long esp0; @@ -116,6 +130,10 @@ unsigned long cr2, trap_no, error_code, segment; /* floating point info */ union i387_union i387; +#if CONFIG_HARDCNTR +/* hardware counters info */ + struct hardware_counters_struct hardcntr; +#endif /* virtual 86 mode info */ struct vm86_struct * vm86_info; unsigned long screen_bitmap; @@ -125,6 +143,24 @@ #define INIT_MMAP \ { &init_mm, 0, 0, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, NULL, &init= _mm.mmap } =

+#if CONFIG_HARDCNTR +#define INIT_TSS { \ + 0,0, \ + sizeof(init_stack) + (long) &init_stack, \ + KERNEL_DS, 0, \ + 0,0,0,0,0,0, \ + (long) &swapper_pg_dir - PAGE_OFFSET, \ + 0,0,0,0,0,0,0,0,0,0, \ + USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0, \ + _LDT(0),0, \ + 0, 0x8000, \ + {~0, }, /* ioperm */ \ + _TSS(0), 0, 0, 0, KERNEL_DS, \ + { { 0, }, }, /* 387 state */ \ + { 0, 0, { 0, }, { 0, } }, /* hardware counters state */ \ + NULL, 0, 0, 0, 0, 0 /* vm86_info */, \ +} +#else /* CONFIG_HARDCNTR */ #define INIT_TSS { \ 0,0, \ sizeof(init_stack) + (long) &init_stack, \ @@ -140,6 +176,7 @@ { { 0, }, }, /* 387 state */ \ NULL, 0, 0, 0, 0, 0 /* vm86_info */, \ } +#endif /* CONFIG_HARDCNTR */ =

#define start_thread(regs, new_eip, new_esp) do {\ unsigned long seg =3D USER_DS; \ diff -u -N -r linux-2.1.65-clean/include/linux/proc_fs.h linux-2.1.65-har= dcntr/include/linux/proc_fs.h --- linux-2.1.65-clean/include/linux/proc_fs.h Tue Nov 18 17:34:28 1997 +++ linux-2.1.65-hardcntr/include/linux/proc_fs.h Mon Nov 24 17:46:34 199= 7 @@ -68,13 +68,19 @@ PROC_PID_STAT, PROC_PID_STATM, PROC_PID_MAPS, +#if CONFIG_HARDCNTR + PROC_PID_HARDCNTR, +#endif #if CONFIG_AP1000 PROC_PID_RINGBUF, #endif }; =

enum pid_subdirectory_inos { - PROC_PID_FD_DIR =3D 1 + PROC_PID_FD_DIR =3D 1, +#if CONFIG_HARDCNTR + PROC_PID_HARDCNTR_DIR, +#endif }; =

enum net_directory_inos { @@ -263,6 +269,9 @@ extern struct proc_dir_entry proc_pid; extern struct proc_dir_entry proc_pid_fd; extern struct proc_dir_entry proc_mca; +#if CONFIG_HARDCNTR +extern struct proc_dir_entry proc_pid_hardcntr; +#endif =

extern struct inode_operations proc_scsi_inode_operations; =

@@ -367,6 +376,9 @@ extern struct inode_operations proc_kmsg_inode_operations; extern struct inode_operations proc_link_inode_operations; extern struct inode_operations proc_fd_inode_operations; +#if CONFIG_HARDCNTR +extern struct inode_operations proc_hardcntr_dir_inode_op; +#endif #if CONFIG_AP1000 extern struct inode_operations proc_ringbuf_inode_operations; #endif diff -u -N -r linux-2.1.65-clean/init/main.c linux-2.1.65-hardcntr/init/m= ain.c --- linux-2.1.65-clean/init/main.c Tue Nov 4 18:17:30 1997 +++ linux-2.1.65-hardcntr/init/main.c Fri Nov 21 21:24:50 1997 @@ -75,6 +75,9 @@ extern long powermac_init(unsigned long, unsigned long); extern void sysctl_init(void); extern void filescache_init(void); +#if CONFIG_HARDCNTR +extern long hardcntr_init(long, long); +#endif =

extern void smp_setup(char *str, int *ints); extern void no_scroll(char *str, int *ints); @@ -928,6 +931,9 @@ #endif #ifdef CONFIG_MCA memory_start =3D mca_init(memory_start,memory_end); +#endif +#if CONFIG_HARDCNTR + memory_start =3D hardcntr_init(memory_start,memory_end); #endif memory_start =3D kmem_cache_init(memory_start, memory_end); sti(); diff -u -N -r linux-2.1.65-clean/kernel/sched.c linux-2.1.65-hardcntr/ker= nel/sched.c --- linux-2.1.65-clean/kernel/sched.c Fri Oct 17 22:26:57 1997 +++ linux-2.1.65-hardcntr/kernel/sched.c Mon Nov 24 10:15:15 1997 @@ -89,6 +89,10 @@ =

extern void mem_use(void); =

+#if CONFIG_HARDCNTR +extern void switch_hardcntr(struct task_struct *prev, struct task_struct= *next); +#endif + unsigned long volatile jiffies=3D0; =

/* @@ -477,6 +481,9 @@ add_timer(&timer); } get_mmu_context(next); +#if CONFIG_HARDCNTR + switch_hardcntr(prev,next); +#endif switch_to(prev,next); =

if (timeout)

--Multipart_Wed_Nov_26_16:10:43_1997-1--