Import 2.1.118
[davej-history.git] / arch / ppc / kernel / ppc_htab.c
blobc1445d39369fb3212b265f356a77f82249e5fa12
1 /*
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>
23 #include <asm/mmu.h>
24 #include <asm/processor.h>
25 #include <asm/residual.h>
26 #include <asm/io.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 */
47 NULL, /* readdir */
48 NULL, /* poll */
49 NULL, /* ioctl */
50 NULL, /* mmap */
51 NULL, /* no special open code */
52 NULL, /* flush */
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 */
62 NULL, /* create */
63 NULL, /* lookup */
64 NULL, /* link */
65 NULL, /* unlink */
66 NULL, /* symlink */
67 NULL, /* mkdir */
68 NULL, /* rmdir */
69 NULL, /* mknod */
70 NULL, /* rename */
71 NULL, /* readlink */
72 NULL, /* follow_link */
73 NULL, /* readpage */
74 NULL, /* writepage */
75 NULL, /* bmap */
76 NULL, /* truncate */
77 NULL /* permission */
80 /* these will go into processor.h when I'm done debugging -- Cort */
81 #define MMCR0 952
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)
90 #define PMC1 953
91 #define PMC2 954
93 char *pmc1_lookup(unsigned long mmcr0)
95 switch ( mmcr0 & (0x7f<<7) )
97 case 0x0:
98 return "none";
99 case MMCR0_PMC1_CYCLES:
100 return "cycles";
101 case MMCR0_PMC1_ICACHEMISS:
102 return "ic miss";
103 case MMCR0_PMC1_DTLB:
104 return "dtlb miss";
105 default:
106 return "unknown";
110 char *pmc2_lookup(unsigned long mmcr0)
112 switch ( mmcr0 & 0x3f )
114 case 0x0:
115 return "none";
116 case MMCR0_PMC2_CYCLES:
117 return "cycles";
118 case MMCR0_PMC2_DCACHEMISS:
119 return "dc miss";
120 case MMCR0_PMC2_ITLB:
121 return "itlb miss";
122 case MMCR0_PMC2_LOADMISSTIME:
123 return "load miss time";
124 default:
125 return "unknown";
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;
138 int n = 0, valid;
139 unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0;
140 PTE *ptr;
141 struct task_struct *p;
142 char buffer[512];
144 if (count < 0)
145 return -EINVAL;
147 switch ( _get_PVR()>>16 )
149 case 4: /* 604 */
150 case 9: /* 604e */
151 case 10: /* 604ev5 */
152 asm volatile ("mfspr %0,952 \n\t"
153 "mfspr %1,953 \n\t"
154 "mfspr %2,954 \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 ",
159 mmcr0,
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));
167 break;
168 default:
169 break;
173 /* if we don't have a htab */
174 if ( Hash_size == 0 )
176 n += sprintf( buffer + n, "No Hash Table used\n");
177 goto return_string;
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
185 * -- Cort
187 for ( ptr = Hash ; ptr < Hash_end ; ptr++)
189 if (ptr->v)
191 /* make sure someone is using this context/vsid */
192 valid = 0;
193 for_each_task(p)
195 if ( (ptr->vsid >> 4) == p->mm->context )
197 valid = 1;
198 break;
201 if ( !valid )
203 zombie_ptes++;
204 continue;
206 /* user not allowed read or write */
207 if (ptr->pp == PP_RWXX)
208 kptes++;
209 else
210 uptes++;
211 if (ptr->h == 1)
212 overflow++;
216 n += sprintf( buffer + n,
217 "PTE Hash Table Information\n"
218 "Size\t\t: %luKb\n"
219 "Buckets\t\t: %lu\n"
220 "Address\t\t: %08lx\n"
221 "Entries\t\t: %lu\n"
222 "User ptes\t: %u\n"
223 "Kernel ptes\t: %u\n"
224 "Overflows\t: %u\n"
225 "Zombies\t\t: %u\n"
226 "Percent full\t: %%%lu\n",
227 (unsigned long)(Hash_size>>10),
228 (Hash_size/(sizeof(PTE)*8)),
229 (unsigned long)Hash,
230 Hash_size/sizeof(PTE),
231 uptes,
232 kptes,
233 overflow,
234 zombie_ptes,
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);
243 return_string:
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))
249 return 0;
250 if (n > strlen(buffer) - *ppos)
251 n = strlen(buffer) - *ppos;
252 copy_to_user(buf, buffer + *ppos, n);
253 *ppos += n;
254 return 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)
263 #ifndef CONFIG_8xx
264 unsigned long tmp;
265 if ( current->uid != 0 )
266 return -EACCES;
267 /* don't set the htab size for now */
268 if ( !strncmp( buffer, "size ", 5) )
269 return -EBUSY;
271 /* turn off performance monitoring */
272 if ( !strncmp( buffer, "off", 3) )
274 switch ( _get_PVR()>>16 )
276 case 4: /* 604 */
277 case 9: /* 604e */
278 case 10: /* 604ev5 */
279 asm volatile ("mtspr %0, %3 \n\t"
280 "mtspr %1, %3 \n\t"
281 "mtspr %2, %3 \n\t"
282 :: "i" (MMCR0), "i" (PMC1), "i" (PMC2), "r" (0));
283 break;
284 default:
285 break;
290 if ( !strncmp( buffer, "reset", 5) )
292 switch ( _get_PVR()>>16 )
294 case 4: /* 604 */
295 case 9: /* 604e */
296 case 10: /* 604ev5 */
297 /* reset PMC1 and PMC2 */
298 asm volatile (
299 "mtspr 953, %0 \n\t"
300 "mtspr 954, %0 \n\t"
301 :: "r" (0));
302 break;
303 default:
304 break;
306 htab_reloads = 0;
307 htab_evicts = 0;
308 pte_misses = 0;
309 pte_errors = 0;
312 if ( !strncmp( buffer, "user", 4) )
314 switch ( _get_PVR()>>16 )
316 case 4: /* 604 */
317 case 9: /* 604e */
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);
322 tmp |= 0x20000000;
323 asm volatile (
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) );
329 break;
330 default:
331 break;
335 if ( !strncmp( buffer, "kernel", 6) )
337 switch ( _get_PVR()>>16 )
339 case 4: /* 604 */
340 case 9: /* 604e */
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);
345 tmp |= 0x40000000;
346 asm volatile (
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) );
352 break;
353 default:
354 break;
358 /* PMC1 values */
359 if ( !strncmp( buffer, "dtlb", 4) )
361 switch ( _get_PVR()>>16 )
363 case 4: /* 604 */
364 case 9: /* 604e */
365 case 10: /* 604ev5 */
366 /* setup mmcr0 and clear the correct pmc */
367 asm("mfspr %0,%1\n\t" : "=r" (tmp) : "i" (MMCR0));
368 tmp &= ~(0x7f<<7);
369 tmp |= MMCR0_PMC1_DTLB;
370 asm volatile (
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 )
382 case 4: /* 604 */
383 case 9: /* 604e */
384 case 10: /* 604ev5 */
385 /* setup mmcr0 and clear the correct pmc */
386 asm("mfspr %0,%1\n\t" : "=r" (tmp) : "i" (MMCR0));
387 tmp &= ~(0x7f<<7);
388 tmp |= MMCR0_PMC1_ICACHEMISS;
389 asm volatile (
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));
397 /* PMC2 values */
398 if ( !strncmp( buffer, "load miss time", 14) )
400 switch ( _get_PVR()>>16 )
402 case 4: /* 604 */
403 case 9: /* 604e */
404 case 10: /* 604ev5 */
405 /* setup mmcr0 and clear the correct pmc */
406 asm volatile(
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 */
412 : "=r" (tmp)
413 : "i" (MMCR0), "i" (MMCR0_PMC2_LOADMISSTIME),
414 "i" (PMC2), "r" (0) );
418 if ( !strncmp( buffer, "itlb", 4) )
420 switch ( _get_PVR()>>16 )
422 case 4: /* 604 */
423 case 9: /* 604e */
424 case 10: /* 604ev5 */
425 /* setup mmcr0 and clear the correct pmc */
426 asm volatile(
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 */
432 : "=r" (tmp)
433 : "i" (MMCR0), "i" (MMCR0_PMC2_ITLB),
434 "i" (PMC2), "r" (0) );
438 if ( !strncmp( buffer, "dc miss", 7) )
440 switch ( _get_PVR()>>16 )
442 case 4: /* 604 */
443 case 9: /* 604e */
444 case 10: /* 604ev5 */
445 /* setup mmcr0 and clear the correct pmc */
446 asm volatile(
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 */
452 : "=r" (tmp)
453 : "i" (MMCR0), "i" (MMCR0_PMC2_DCACHEMISS),
454 "i" (PMC2), "r" (0) );
459 return count;
461 #if 0 /* resizing htab is a bit difficult right now -- Cort */
462 unsigned long size;
463 extern void reset_SDR1(void);
465 /* only know how to set size right now */
466 if ( strncmp( buffer, "size ", 5) )
467 return -EINVAL;
469 size = simple_strtoul( &buffer[5], NULL, 10 );
471 /* only allow to shrink */
472 if ( size >= Hash_size>>10 )
473 return -EINVAL;
475 /* minimum size of htab */
476 if ( size < 64 )
477 return -EINVAL;
479 /* make sure it's a multiple of 64k */
480 if ( size % 64 )
481 return -EINVAL;
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.
490 * -- Cort
492 Hash_size = size<<10;
493 Hash_mask = (Hash_size >> 6) - 1;
494 _SDR1 = __pa(Hash) | (Hash_mask >> 10);
495 flush_tlb_all();
497 reset_SDR1();
498 #endif
499 return count;
500 #else /* CONFIG_8xx */
501 return 0;
502 #endif /* CONFIG_8xx */
506 static long long
507 ppc_htab_lseek(struct file * file, loff_t offset, int orig)
509 switch (orig) {
510 case 0:
511 file->f_pos = offset;
512 return(file->f_pos);
513 case 1:
514 file->f_pos += offset;
515 return(file->f_pos);
516 case 2:
517 return(-EINVAL);
518 default:
519 return(-EINVAL);