2 * $Id: ppc_htab.c,v 1.21 1998/05/13 22:34:55 cort Exp $
4 * PowerPC hash table management proc entry. Will show information
5 * about the current hash table and will allow changes to it.
7 * Written by Cort Dougan (cort@cs.nmt.edu)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
15 #include <linux/config.h>
16 #include <linux/errno.h>
17 #include <linux/sched.h>
18 #include <linux/proc_fs.h>
19 #include <linux/stat.h>
21 #include <asm/uaccess.h>
22 #include <asm/bitops.h>
24 #include <asm/processor.h>
25 #include <asm/residual.h>
27 #include <asm/pgtable.h>
29 static ssize_t
ppc_htab_read(struct file
* file
, char * buf
,
30 size_t count
, loff_t
*ppos
);
31 static ssize_t
ppc_htab_write(struct file
* file
, const char * buffer
,
32 size_t count
, loff_t
*ppos
);
33 static long long ppc_htab_lseek(struct file
* file
, loff_t offset
, int orig
);
35 extern PTE
*Hash
, *Hash_end
;
36 extern unsigned long Hash_size
, Hash_mask
;
37 extern unsigned long _SDR1
;
38 extern unsigned long htab_reloads
;
39 extern unsigned long htab_evicts
;
40 extern unsigned long pte_misses
;
41 extern unsigned long pte_errors
;
43 static struct file_operations ppc_htab_operations
= {
44 ppc_htab_lseek
, /* lseek */
45 ppc_htab_read
, /* read */
46 ppc_htab_write
, /* write */
51 NULL
, /* no special open code */
53 NULL
, /* no special release code */
54 NULL
/* can't fsync */
58 * proc files can do almost nothing..
60 struct inode_operations proc_ppc_htab_inode_operations
= {
61 &ppc_htab_operations
, /* default proc file-ops */
72 NULL
, /* follow_link */
80 /* these will go into processor.h when I'm done debugging -- Cort */
82 #define MMCR0_PMC1_CYCLES (0x1<<7)
83 #define MMCR0_PMC1_ICACHEMISS (0x5<<7)
84 #define MMCR0_PMC1_DTLB (0x6<<7)
85 #define MMCR0_PMC2_DCACHEMISS (0x6)
86 #define MMCR0_PMC2_CYCLES (0x1)
87 #define MMCR0_PMC2_ITLB (0x7)
88 #define MMCR0_PMC2_LOADMISSTIME (0x5)
93 char *pmc1_lookup(unsigned long mmcr0
)
95 switch ( mmcr0
& (0x7f<<7) )
99 case MMCR0_PMC1_CYCLES
:
101 case MMCR0_PMC1_ICACHEMISS
:
103 case MMCR0_PMC1_DTLB
:
110 char *pmc2_lookup(unsigned long mmcr0
)
112 switch ( mmcr0
& 0x3f )
116 case MMCR0_PMC2_CYCLES
:
118 case MMCR0_PMC2_DCACHEMISS
:
120 case MMCR0_PMC2_ITLB
:
122 case MMCR0_PMC2_LOADMISSTIME
:
123 return "load miss time";
130 * print some useful info about the hash table. This function
131 * is _REALLY_ slow (see the nested for loops below) but nothing
132 * in here should be really timing critical. -- Cort
134 static ssize_t
ppc_htab_read(struct file
* file
, char * buf
,
135 size_t count
, loff_t
*ppos
)
137 unsigned long mmcr0
= 0, pmc1
= 0, pmc2
= 0;
139 unsigned int kptes
= 0, overflow
= 0, uptes
= 0, zombie_ptes
= 0;
141 struct task_struct
*p
;
147 switch ( _get_PVR()>>16 )
151 case 10: /* 604ev5 */
152 asm volatile ("mfspr %0,952 \n\t"
155 : "=r" (mmcr0
), "=r" (pmc1
), "=r" (pmc2
) );
156 n
+= sprintf( buffer
+ n
,
157 "604 Performance Monitoring\n"
158 "MMCR0\t\t: %08lx %s%s ",
160 ( mmcr0
>>28 & 0x2 ) ? "(user mode counted)" : "",
161 ( mmcr0
>>28 & 0x4 ) ? "(kernel mode counted)" : "");
162 n
+= sprintf( buffer
+ n
,
163 "\nPMC1\t\t: %08lx (%s)\n"
164 "PMC2\t\t: %08lx (%s)\n",
165 pmc1
, pmc1_lookup(mmcr0
),
166 pmc2
, pmc2_lookup(mmcr0
));
173 /* if we don't have a htab */
174 if ( Hash_size
== 0 )
176 n
+= sprintf( buffer
+ n
, "No Hash Table used\n");
181 * compute user/kernel pte's table this info can be
182 * misleading since there can be valid (v bit set) entries
183 * in the table but their vsid is used by no process (mm->context)
184 * due to the way tlb invalidation is handled on the ppc
187 for ( ptr
= Hash
; ptr
< Hash_end
; ptr
++)
191 /* make sure someone is using this context/vsid */
195 if ( (ptr
->vsid
>> 4) == p
->mm
->context
)
206 /* user not allowed read or write */
207 if (ptr
->pp
== PP_RWXX
)
216 n
+= sprintf( buffer
+ n
,
217 "PTE Hash Table Information\n"
220 "Address\t\t: %08lx\n"
223 "Kernel ptes\t: %u\n"
226 "Percent full\t: %%%lu\n",
227 (unsigned long)(Hash_size
>>10),
228 (Hash_size
/(sizeof(PTE
)*8)),
230 Hash_size
/sizeof(PTE
),
235 ((kptes
+uptes
)*100) / (Hash_size
/sizeof(PTE
))
238 n
+= sprintf( buffer
+ n
,
239 "Reloads\t\t: %08lx\n"
240 "Evicts\t\t: %08lx\n",
241 htab_reloads
, htab_evicts
);
244 n
+= sprintf( buffer
+ n
,
245 "Non-error misses: %08lx\n"
246 "Error misses\t: %08lx\n",
247 pte_misses
, pte_errors
);
248 if (*ppos
>= strlen(buffer
))
250 if (n
> strlen(buffer
) - *ppos
)
251 n
= strlen(buffer
) - *ppos
;
252 copy_to_user(buf
, buffer
+ *ppos
, n
);
258 * Allow user to define performance counters and resize the hash table
260 static ssize_t
ppc_htab_write(struct file
* file
, const char * buffer
,
261 size_t count
, loff_t
*ppos
)
265 if ( current
->uid
!= 0 )
267 /* don't set the htab size for now */
268 if ( !strncmp( buffer
, "size ", 5) )
271 /* turn off performance monitoring */
272 if ( !strncmp( buffer
, "off", 3) )
274 switch ( _get_PVR()>>16 )
278 case 10: /* 604ev5 */
279 asm volatile ("mtspr %0, %3 \n\t"
282 :: "i" (MMCR0
), "i" (PMC1
), "i" (PMC2
), "r" (0));
290 if ( !strncmp( buffer
, "reset", 5) )
292 switch ( _get_PVR()>>16 )
296 case 10: /* 604ev5 */
297 /* reset PMC1 and PMC2 */
312 if ( !strncmp( buffer
, "user", 4) )
314 switch ( _get_PVR()>>16 )
318 case 10: /* 604ev5 */
319 /* setup mmcr0 and clear the correct pmc */
320 asm("mfspr %0,%1\n\t" : "=r" (tmp
) : "i" (MMCR0
));
321 tmp
&= ~(0x60000000);
324 "mtspr %1,%0 \n\t" /* set new mccr0 */
325 "mtspr %3,%4 \n\t" /* reset the pmc */
326 "mtspr %5,%4 \n\t" /* reset the pmc2 */
327 :: "r" (tmp
), "i" (MMCR0
), "i" (0),
328 "i" (PMC1
), "r" (0), "i"(PMC2
) );
335 if ( !strncmp( buffer
, "kernel", 6) )
337 switch ( _get_PVR()>>16 )
341 case 10: /* 604ev5 */
342 /* setup mmcr0 and clear the correct pmc */
343 asm("mfspr %0,%1\n\t" : "=r" (tmp
) : "i" (MMCR0
));
344 tmp
&= ~(0x60000000);
347 "mtspr %1,%0 \n\t" /* set new mccr0 */
348 "mtspr %3,%4 \n\t" /* reset the pmc */
349 "mtspr %5,%4 \n\t" /* reset the pmc2 */
350 :: "r" (tmp
), "i" (MMCR0
), "i" (0),
351 "i" (PMC1
), "r" (0), "i"(PMC2
) );
359 if ( !strncmp( buffer
, "dtlb", 4) )
361 switch ( _get_PVR()>>16 )
365 case 10: /* 604ev5 */
366 /* setup mmcr0 and clear the correct pmc */
367 asm("mfspr %0,%1\n\t" : "=r" (tmp
) : "i" (MMCR0
));
369 tmp
|= MMCR0_PMC1_DTLB
;
371 "mtspr %1,%0 \n\t" /* set new mccr0 */
372 "mtspr %3,%4 \n\t" /* reset the pmc */
373 :: "r" (tmp
), "i" (MMCR0
), "i" (MMCR0_PMC1_DTLB
),
374 "i" (PMC1
), "r" (0) );
378 if ( !strncmp( buffer
, "ic miss", 7) )
380 switch ( _get_PVR()>>16 )
384 case 10: /* 604ev5 */
385 /* setup mmcr0 and clear the correct pmc */
386 asm("mfspr %0,%1\n\t" : "=r" (tmp
) : "i" (MMCR0
));
388 tmp
|= MMCR0_PMC1_ICACHEMISS
;
390 "mtspr %1,%0 \n\t" /* set new mccr0 */
391 "mtspr %3,%4 \n\t" /* reset the pmc */
392 :: "r" (tmp
), "i" (MMCR0
),
393 "i" (MMCR0_PMC1_ICACHEMISS
), "i" (PMC1
), "r" (0));
398 if ( !strncmp( buffer
, "load miss time", 14) )
400 switch ( _get_PVR()>>16 )
404 case 10: /* 604ev5 */
405 /* setup mmcr0 and clear the correct pmc */
407 "mfspr %0,%1\n\t" /* get current mccr0 */
408 "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
409 "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
410 "mtspr %1,%0 \n\t" /* set new mccr0 */
411 "mtspr %3,%4 \n\t" /* reset the pmc */
413 : "i" (MMCR0
), "i" (MMCR0_PMC2_LOADMISSTIME
),
414 "i" (PMC2
), "r" (0) );
418 if ( !strncmp( buffer
, "itlb", 4) )
420 switch ( _get_PVR()>>16 )
424 case 10: /* 604ev5 */
425 /* setup mmcr0 and clear the correct pmc */
427 "mfspr %0,%1\n\t" /* get current mccr0 */
428 "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
429 "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
430 "mtspr %1,%0 \n\t" /* set new mccr0 */
431 "mtspr %3,%4 \n\t" /* reset the pmc */
433 : "i" (MMCR0
), "i" (MMCR0_PMC2_ITLB
),
434 "i" (PMC2
), "r" (0) );
438 if ( !strncmp( buffer
, "dc miss", 7) )
440 switch ( _get_PVR()>>16 )
444 case 10: /* 604ev5 */
445 /* setup mmcr0 and clear the correct pmc */
447 "mfspr %0,%1\n\t" /* get current mccr0 */
448 "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
449 "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
450 "mtspr %1,%0 \n\t" /* set new mccr0 */
451 "mtspr %3,%4 \n\t" /* reset the pmc */
453 : "i" (MMCR0
), "i" (MMCR0_PMC2_DCACHEMISS
),
454 "i" (PMC2
), "r" (0) );
461 #if 0 /* resizing htab is a bit difficult right now -- Cort */
463 extern void reset_SDR1(void);
465 /* only know how to set size right now */
466 if ( strncmp( buffer
, "size ", 5) )
469 size
= simple_strtoul( &buffer
[5], NULL
, 10 );
471 /* only allow to shrink */
472 if ( size
>= Hash_size
>>10 )
475 /* minimum size of htab */
479 /* make sure it's a multiple of 64k */
483 printk("Hash table resize to %luk\n", size
);
485 * We need to rehash all kernel entries for the new htab size.
486 * Kernel only since we do a flush_tlb_all(). Since it's kernel
487 * we only need to bother with vsids 0-15. To avoid problems of
488 * clobbering un-rehashed values we put the htab at a new spot
489 * and put everything there.
492 Hash_size
= size
<<10;
493 Hash_mask
= (Hash_size
>> 6) - 1;
494 _SDR1
= __pa(Hash
) | (Hash_mask
>> 10);
500 #else /* CONFIG_8xx */
502 #endif /* CONFIG_8xx */
507 ppc_htab_lseek(struct file
* file
, loff_t offset
, int orig
)
511 file
->f_pos
= offset
;
514 file
->f_pos
+= offset
;