diff options
-rw-r--r-- | fs/proc/task_mmu.c | 127 | ||||
-rw-r--r-- | mm/mempolicy.c | 138 |
2 files changed, 142 insertions, 123 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 50bd5a8f0446..0eaad41f4658 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -390,129 +390,12 @@ struct seq_operations proc_pid_smaps_op = { | |||
390 | }; | 390 | }; |
391 | 391 | ||
392 | #ifdef CONFIG_NUMA | 392 | #ifdef CONFIG_NUMA |
393 | 393 | extern int show_numa_map(struct seq_file *m, void *v); | |
394 | struct numa_maps { | ||
395 | unsigned long pages; | ||
396 | unsigned long anon; | ||
397 | unsigned long mapped; | ||
398 | unsigned long mapcount_max; | ||
399 | unsigned long node[MAX_NUMNODES]; | ||
400 | }; | ||
401 | |||
402 | /* | ||
403 | * Calculate numa node maps for a vma | ||
404 | */ | ||
405 | static struct numa_maps *get_numa_maps(struct vm_area_struct *vma) | ||
406 | { | ||
407 | int i; | ||
408 | struct page *page; | ||
409 | unsigned long vaddr; | ||
410 | struct numa_maps *md = kmalloc(sizeof(struct numa_maps), GFP_KERNEL); | ||
411 | |||
412 | if (!md) | ||
413 | return NULL; | ||
414 | md->pages = 0; | ||
415 | md->anon = 0; | ||
416 | md->mapped = 0; | ||
417 | md->mapcount_max = 0; | ||
418 | for_each_node(i) | ||
419 | md->node[i] =0; | ||
420 | |||
421 | for (vaddr = vma->vm_start; vaddr < vma->vm_end; vaddr += PAGE_SIZE) { | ||
422 | page = follow_page(vma, vaddr, 0); | ||
423 | if (page) { | ||
424 | int count = page_mapcount(page); | ||
425 | |||
426 | if (count) | ||
427 | md->mapped++; | ||
428 | if (count > md->mapcount_max) | ||
429 | md->mapcount_max = count; | ||
430 | md->pages++; | ||
431 | if (PageAnon(page)) | ||
432 | md->anon++; | ||
433 | md->node[page_to_nid(page)]++; | ||
434 | } | ||
435 | cond_resched(); | ||
436 | } | ||
437 | return md; | ||
438 | } | ||
439 | |||
440 | static int show_numa_map(struct seq_file *m, void *v) | ||
441 | { | ||
442 | struct task_struct *task = m->private; | ||
443 | struct vm_area_struct *vma = v; | ||
444 | struct mempolicy *pol; | ||
445 | struct numa_maps *md; | ||
446 | struct zone **z; | ||
447 | int n; | ||
448 | int first; | ||
449 | |||
450 | if (!vma->vm_mm) | ||
451 | return 0; | ||
452 | |||
453 | md = get_numa_maps(vma); | ||
454 | if (!md) | ||
455 | return 0; | ||
456 | |||
457 | seq_printf(m, "%08lx", vma->vm_start); | ||
458 | pol = get_vma_policy(task, vma, vma->vm_start); | ||
459 | /* Print policy */ | ||
460 | switch (pol->policy) { | ||
461 | case MPOL_PREFERRED: | ||
462 | seq_printf(m, " prefer=%d", pol->v.preferred_node); | ||
463 | break; | ||
464 | case MPOL_BIND: | ||
465 | seq_printf(m, " bind={"); | ||
466 | first = 1; | ||
467 | for (z = pol->v.zonelist->zones; *z; z++) { | ||
468 | |||
469 | if (!first) | ||
470 | seq_putc(m, ','); | ||
471 | else | ||
472 | first = 0; | ||
473 | seq_printf(m, "%d/%s", (*z)->zone_pgdat->node_id, | ||
474 | (*z)->name); | ||
475 | } | ||
476 | seq_putc(m, '}'); | ||
477 | break; | ||
478 | case MPOL_INTERLEAVE: | ||
479 | seq_printf(m, " interleave={"); | ||
480 | first = 1; | ||
481 | for_each_node(n) { | ||
482 | if (node_isset(n, pol->v.nodes)) { | ||
483 | if (!first) | ||
484 | seq_putc(m,','); | ||
485 | else | ||
486 | first = 0; | ||
487 | seq_printf(m, "%d",n); | ||
488 | } | ||
489 | } | ||
490 | seq_putc(m, '}'); | ||
491 | break; | ||
492 | default: | ||
493 | seq_printf(m," default"); | ||
494 | break; | ||
495 | } | ||
496 | seq_printf(m, " MaxRef=%lu Pages=%lu Mapped=%lu", | ||
497 | md->mapcount_max, md->pages, md->mapped); | ||
498 | if (md->anon) | ||
499 | seq_printf(m," Anon=%lu",md->anon); | ||
500 | |||
501 | for_each_online_node(n) { | ||
502 | if (md->node[n]) | ||
503 | seq_printf(m, " N%d=%lu", n, md->node[n]); | ||
504 | } | ||
505 | seq_putc(m, '\n'); | ||
506 | kfree(md); | ||
507 | if (m->count < m->size) /* vma is copied successfully */ | ||
508 | m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; | ||
509 | return 0; | ||
510 | } | ||
511 | 394 | ||
512 | struct seq_operations proc_pid_numa_maps_op = { | 395 | struct seq_operations proc_pid_numa_maps_op = { |
513 | .start = m_start, | 396 | .start = m_start, |
514 | .next = m_next, | 397 | .next = m_next, |
515 | .stop = m_stop, | 398 | .stop = m_stop, |
516 | .show = show_numa_map | 399 | .show = show_numa_map |
517 | }; | 400 | }; |
518 | #endif | 401 | #endif |
diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 270e9a39ec15..44b9d69900bc 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c | |||
@@ -84,6 +84,8 @@ | |||
84 | #include <linux/compat.h> | 84 | #include <linux/compat.h> |
85 | #include <linux/mempolicy.h> | 85 | #include <linux/mempolicy.h> |
86 | #include <linux/swap.h> | 86 | #include <linux/swap.h> |
87 | #include <linux/seq_file.h> | ||
88 | #include <linux/proc_fs.h> | ||
87 | 89 | ||
88 | #include <asm/tlbflush.h> | 90 | #include <asm/tlbflush.h> |
89 | #include <asm/uaccess.h> | 91 | #include <asm/uaccess.h> |
@@ -91,6 +93,7 @@ | |||
91 | /* Internal flags */ | 93 | /* Internal flags */ |
92 | #define MPOL_MF_DISCONTIG_OK (MPOL_MF_INTERNAL << 0) /* Skip checks for continuous vmas */ | 94 | #define MPOL_MF_DISCONTIG_OK (MPOL_MF_INTERNAL << 0) /* Skip checks for continuous vmas */ |
93 | #define MPOL_MF_INVERT (MPOL_MF_INTERNAL << 1) /* Invert check for nodemask */ | 95 | #define MPOL_MF_INVERT (MPOL_MF_INTERNAL << 1) /* Invert check for nodemask */ |
96 | #define MPOL_MF_STATS (MPOL_MF_INTERNAL << 2) /* Gather statistics */ | ||
94 | 97 | ||
95 | static kmem_cache_t *policy_cache; | 98 | static kmem_cache_t *policy_cache; |
96 | static kmem_cache_t *sn_cache; | 99 | static kmem_cache_t *sn_cache; |
@@ -228,6 +231,8 @@ static void migrate_page_add(struct vm_area_struct *vma, | |||
228 | } | 231 | } |
229 | } | 232 | } |
230 | 233 | ||
234 | static void gather_stats(struct page *, void *); | ||
235 | |||
231 | /* Scan through pages checking if pages follow certain conditions. */ | 236 | /* Scan through pages checking if pages follow certain conditions. */ |
232 | static int check_pte_range(struct vm_area_struct *vma, pmd_t *pmd, | 237 | static int check_pte_range(struct vm_area_struct *vma, pmd_t *pmd, |
233 | unsigned long addr, unsigned long end, | 238 | unsigned long addr, unsigned long end, |
@@ -252,7 +257,9 @@ static int check_pte_range(struct vm_area_struct *vma, pmd_t *pmd, | |||
252 | if (node_isset(nid, *nodes) == !!(flags & MPOL_MF_INVERT)) | 257 | if (node_isset(nid, *nodes) == !!(flags & MPOL_MF_INVERT)) |
253 | continue; | 258 | continue; |
254 | 259 | ||
255 | if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) | 260 | if (flags & MPOL_MF_STATS) |
261 | gather_stats(page, private); | ||
262 | else if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) | ||
256 | migrate_page_add(vma, page, private, flags); | 263 | migrate_page_add(vma, page, private, flags); |
257 | else | 264 | else |
258 | break; | 265 | break; |
@@ -1460,3 +1467,132 @@ void numa_policy_rebind(const nodemask_t *old, const nodemask_t *new) | |||
1460 | { | 1467 | { |
1461 | rebind_policy(current->mempolicy, old, new); | 1468 | rebind_policy(current->mempolicy, old, new); |
1462 | } | 1469 | } |
1470 | |||
1471 | /* | ||
1472 | * Display pages allocated per node and memory policy via /proc. | ||
1473 | */ | ||
1474 | |||
1475 | static const char *policy_types[] = { "default", "prefer", "bind", | ||
1476 | "interleave" }; | ||
1477 | |||
1478 | /* | ||
1479 | * Convert a mempolicy into a string. | ||
1480 | * Returns the number of characters in buffer (if positive) | ||
1481 | * or an error (negative) | ||
1482 | */ | ||
1483 | static inline int mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol) | ||
1484 | { | ||
1485 | char *p = buffer; | ||
1486 | int l; | ||
1487 | nodemask_t nodes; | ||
1488 | int mode = pol ? pol->policy : MPOL_DEFAULT; | ||
1489 | |||
1490 | switch (mode) { | ||
1491 | case MPOL_DEFAULT: | ||
1492 | nodes_clear(nodes); | ||
1493 | break; | ||
1494 | |||
1495 | case MPOL_PREFERRED: | ||
1496 | nodes_clear(nodes); | ||
1497 | node_set(pol->v.preferred_node, nodes); | ||
1498 | break; | ||
1499 | |||
1500 | case MPOL_BIND: | ||
1501 | get_zonemask(pol, &nodes); | ||
1502 | break; | ||
1503 | |||
1504 | case MPOL_INTERLEAVE: | ||
1505 | nodes = pol->v.nodes; | ||
1506 | break; | ||
1507 | |||
1508 | default: | ||
1509 | BUG(); | ||
1510 | return -EFAULT; | ||
1511 | } | ||
1512 | |||
1513 | l = strlen(policy_types[mode]); | ||
1514 | if (buffer + maxlen < p + l + 1) | ||
1515 | return -ENOSPC; | ||
1516 | |||
1517 | strcpy(p, policy_types[mode]); | ||
1518 | p += l; | ||
1519 | |||
1520 | if (!nodes_empty(nodes)) { | ||
1521 | if (buffer + maxlen < p + 2) | ||
1522 | return -ENOSPC; | ||
1523 | *p++ = '='; | ||
1524 | p += nodelist_scnprintf(p, buffer + maxlen - p, nodes); | ||
1525 | } | ||
1526 | return p - buffer; | ||
1527 | } | ||
1528 | |||
1529 | struct numa_maps { | ||
1530 | unsigned long pages; | ||
1531 | unsigned long anon; | ||
1532 | unsigned long mapped; | ||
1533 | unsigned long mapcount_max; | ||
1534 | unsigned long node[MAX_NUMNODES]; | ||
1535 | }; | ||
1536 | |||
1537 | static void gather_stats(struct page *page, void *private) | ||
1538 | { | ||
1539 | struct numa_maps *md = private; | ||
1540 | int count = page_mapcount(page); | ||
1541 | |||
1542 | if (count) | ||
1543 | md->mapped++; | ||
1544 | |||
1545 | if (count > md->mapcount_max) | ||
1546 | md->mapcount_max = count; | ||
1547 | |||
1548 | md->pages++; | ||
1549 | |||
1550 | if (PageAnon(page)) | ||
1551 | md->anon++; | ||
1552 | |||
1553 | md->node[page_to_nid(page)]++; | ||
1554 | cond_resched(); | ||
1555 | } | ||
1556 | |||
1557 | int show_numa_map(struct seq_file *m, void *v) | ||
1558 | { | ||
1559 | struct task_struct *task = m->private; | ||
1560 | struct vm_area_struct *vma = v; | ||
1561 | struct numa_maps *md; | ||
1562 | int n; | ||
1563 | char buffer[50]; | ||
1564 | |||
1565 | if (!vma->vm_mm) | ||
1566 | return 0; | ||
1567 | |||
1568 | md = kzalloc(sizeof(struct numa_maps), GFP_KERNEL); | ||
1569 | if (!md) | ||
1570 | return 0; | ||
1571 | |||
1572 | check_pgd_range(vma, vma->vm_start, vma->vm_end, | ||
1573 | &node_online_map, MPOL_MF_STATS, md); | ||
1574 | |||
1575 | if (md->pages) { | ||
1576 | mpol_to_str(buffer, sizeof(buffer), | ||
1577 | get_vma_policy(task, vma, vma->vm_start)); | ||
1578 | |||
1579 | seq_printf(m, "%08lx %s pages=%lu mapped=%lu maxref=%lu", | ||
1580 | vma->vm_start, buffer, md->pages, | ||
1581 | md->mapped, md->mapcount_max); | ||
1582 | |||
1583 | if (md->anon) | ||
1584 | seq_printf(m," anon=%lu",md->anon); | ||
1585 | |||
1586 | for_each_online_node(n) | ||
1587 | if (md->node[n]) | ||
1588 | seq_printf(m, " N%d=%lu", n, md->node[n]); | ||
1589 | |||
1590 | seq_putc(m, '\n'); | ||
1591 | } | ||
1592 | kfree(md); | ||
1593 | |||
1594 | if (m->count < m->size) | ||
1595 | m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; | ||
1596 | return 0; | ||
1597 | } | ||
1598 | |||