aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorHuang Ying <ying.huang@intel.com>2011-07-13 01:14:26 -0400
committerLen Brown <len.brown@intel.com>2011-08-03 11:15:57 -0400
commit152cef40a808d3034e383465b3f7d6783613e458 (patch)
treef0d098fbaea73e9636b02f7904c72e38d381cede /drivers/acpi
parent67eb2e99076708cc790019a6a08ca3e0ae130a3a (diff)
ACPI, APEI, GHES, Error records content based throttle
printk is used by GHES to report hardware errors. Ratelimit is enforced on the printk to avoid too many hardware error reports in kernel log. Because there may be thousands or even millions of corrected hardware errors during system running. Currently, a simple scheme is used. That is, the total number of hardware error reporting is ratelimited. This may cause some issues in practice. For example, there are two kinds of hardware errors occurred in system. One is corrected memory error, because the fault memory address is accessed frequently, there may be hundreds error report per-second. The other is corrected PCIe AER error, it will be reported once per-second. Because they share one ratelimit control structure, it is highly possible that only memory error is reported. To avoid the above issue, an error record content based throttle algorithm is implemented in the patch. Where after the first successful reporting, all error records that are same are throttled for some time, to let other kinds of error records have the opportunity to be reported. In above example, the memory errors will be throttled for some time, after being printked. Then the PCIe AER error will be printked successfully. Signed-off-by: Huang Ying <ying.huang@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/apei/ghes.c184
1 files changed, 177 insertions, 7 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index d1a40218e17e..931410d31a96 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -60,6 +60,21 @@
60 60
61#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 61#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
62 62
63/* This is just an estimation for memory pool allocation */
64#define GHES_ESTATUS_CACHE_AVG_SIZE 512
65
66#define GHES_ESTATUS_CACHES_SIZE 4
67
68#define GHES_ESTATUS_IN_CACHE_MAX_NSEC (10 * NSEC_PER_SEC)
69/* Prevent too many caches are allocated because of RCU */
70#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2)
71
72#define GHES_ESTATUS_CACHE_LEN(estatus_len) \
73 (sizeof(struct ghes_estatus_cache) + (estatus_len))
74#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \
75 ((struct acpi_hest_generic_status *) \
76 ((struct ghes_estatus_cache *)(estatus_cache) + 1))
77
63#define GHES_ESTATUS_NODE_LEN(estatus_len) \ 78#define GHES_ESTATUS_NODE_LEN(estatus_len) \
64 (sizeof(struct ghes_estatus_node) + (estatus_len)) 79 (sizeof(struct ghes_estatus_node) + (estatus_len))
65#define GHES_ESTATUS_FROM_NODE(estatus_node) \ 80#define GHES_ESTATUS_FROM_NODE(estatus_node) \
@@ -94,6 +109,14 @@ struct ghes_estatus_node {
94 struct acpi_hest_generic *generic; 109 struct acpi_hest_generic *generic;
95}; 110};
96 111
112struct ghes_estatus_cache {
113 u32 estatus_len;
114 atomic_t count;
115 struct acpi_hest_generic *generic;
116 unsigned long long time_in;
117 struct rcu_head rcu;
118};
119
97int ghes_disable; 120int ghes_disable;
98module_param_named(disable, ghes_disable, bool, 0); 121module_param_named(disable, ghes_disable, bool, 0);
99 122
@@ -154,6 +177,9 @@ static unsigned long ghes_estatus_pool_size_request;
154static struct llist_head ghes_estatus_llist; 177static struct llist_head ghes_estatus_llist;
155static struct irq_work ghes_proc_irq_work; 178static struct irq_work ghes_proc_irq_work;
156 179
180struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
181static atomic_t ghes_estatus_cache_alloced;
182
157static int ghes_ioremap_init(void) 183static int ghes_ioremap_init(void)
158{ 184{
159 ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, 185 ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
@@ -458,9 +484,9 @@ static void __ghes_print_estatus(const char *pfx,
458 apei_estatus_print(pfx, estatus); 484 apei_estatus_print(pfx, estatus);
459} 485}
460 486
461static void ghes_print_estatus(const char *pfx, 487static int ghes_print_estatus(const char *pfx,
462 const struct acpi_hest_generic *generic, 488 const struct acpi_hest_generic *generic,
463 const struct acpi_hest_generic_status *estatus) 489 const struct acpi_hest_generic_status *estatus)
464{ 490{
465 /* Not more than 2 messages every 5 seconds */ 491 /* Not more than 2 messages every 5 seconds */
466 static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); 492 static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
@@ -471,8 +497,137 @@ static void ghes_print_estatus(const char *pfx,
471 ratelimit = &ratelimit_corrected; 497 ratelimit = &ratelimit_corrected;
472 else 498 else
473 ratelimit = &ratelimit_uncorrected; 499 ratelimit = &ratelimit_uncorrected;
474 if (__ratelimit(ratelimit)) 500 if (__ratelimit(ratelimit)) {
475 __ghes_print_estatus(pfx, generic, estatus); 501 __ghes_print_estatus(pfx, generic, estatus);
502 return 1;
503 }
504 return 0;
505}
506
507/*
508 * GHES error status reporting throttle, to report more kinds of
509 * errors, instead of just most frequently occurred errors.
510 */
511static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
512{
513 u32 len;
514 int i, cached = 0;
515 unsigned long long now;
516 struct ghes_estatus_cache *cache;
517 struct acpi_hest_generic_status *cache_estatus;
518
519 len = apei_estatus_len(estatus);
520 rcu_read_lock();
521 for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
522 cache = rcu_dereference(ghes_estatus_caches[i]);
523 if (cache == NULL)
524 continue;
525 if (len != cache->estatus_len)
526 continue;
527 cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
528 if (memcmp(estatus, cache_estatus, len))
529 continue;
530 atomic_inc(&cache->count);
531 now = sched_clock();
532 if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
533 cached = 1;
534 break;
535 }
536 rcu_read_unlock();
537 return cached;
538}
539
540static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
541 struct acpi_hest_generic *generic,
542 struct acpi_hest_generic_status *estatus)
543{
544 int alloced;
545 u32 len, cache_len;
546 struct ghes_estatus_cache *cache;
547 struct acpi_hest_generic_status *cache_estatus;
548
549 alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
550 if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
551 atomic_dec(&ghes_estatus_cache_alloced);
552 return NULL;
553 }
554 len = apei_estatus_len(estatus);
555 cache_len = GHES_ESTATUS_CACHE_LEN(len);
556 cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
557 if (!cache) {
558 atomic_dec(&ghes_estatus_cache_alloced);
559 return NULL;
560 }
561 cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
562 memcpy(cache_estatus, estatus, len);
563 cache->estatus_len = len;
564 atomic_set(&cache->count, 0);
565 cache->generic = generic;
566 cache->time_in = sched_clock();
567 return cache;
568}
569
570static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
571{
572 u32 len;
573
574 len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
575 len = GHES_ESTATUS_CACHE_LEN(len);
576 gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
577 atomic_dec(&ghes_estatus_cache_alloced);
578}
579
580static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
581{
582 struct ghes_estatus_cache *cache;
583
584 cache = container_of(head, struct ghes_estatus_cache, rcu);
585 ghes_estatus_cache_free(cache);
586}
587
588static void ghes_estatus_cache_add(
589 struct acpi_hest_generic *generic,
590 struct acpi_hest_generic_status *estatus)
591{
592 int i, slot = -1, count;
593 unsigned long long now, duration, period, max_period = 0;
594 struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
595
596 new_cache = ghes_estatus_cache_alloc(generic, estatus);
597 if (new_cache == NULL)
598 return;
599 rcu_read_lock();
600 now = sched_clock();
601 for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
602 cache = rcu_dereference(ghes_estatus_caches[i]);
603 if (cache == NULL) {
604 slot = i;
605 slot_cache = NULL;
606 break;
607 }
608 duration = now - cache->time_in;
609 if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
610 slot = i;
611 slot_cache = cache;
612 break;
613 }
614 count = atomic_read(&cache->count);
615 period = duration / (count + 1);
616 if (period > max_period) {
617 max_period = period;
618 slot = i;
619 slot_cache = cache;
620 }
621 }
622 /* new_cache must be put into array after its contents are written */
623 smp_wmb();
624 if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
625 slot_cache, new_cache) == slot_cache) {
626 if (slot_cache)
627 call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
628 } else
629 ghes_estatus_cache_free(new_cache);
630 rcu_read_unlock();
476} 631}
477 632
478static int ghes_proc(struct ghes *ghes) 633static int ghes_proc(struct ghes *ghes)
@@ -482,9 +637,11 @@ static int ghes_proc(struct ghes *ghes)
482 rc = ghes_read_estatus(ghes, 0); 637 rc = ghes_read_estatus(ghes, 0);
483 if (rc) 638 if (rc)
484 goto out; 639 goto out;
485 ghes_print_estatus(NULL, ghes->generic, ghes->estatus); 640 if (!ghes_estatus_cached(ghes->estatus)) {
641 if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
642 ghes_estatus_cache_add(ghes->generic, ghes->estatus);
643 }
486 ghes_do_proc(ghes->estatus); 644 ghes_do_proc(ghes->estatus);
487
488out: 645out:
489 ghes_clear_estatus(ghes); 646 ghes_clear_estatus(ghes);
490 return 0; 647 return 0;
@@ -546,6 +703,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
546{ 703{
547 struct llist_node *llnode, *next, *tail = NULL; 704 struct llist_node *llnode, *next, *tail = NULL;
548 struct ghes_estatus_node *estatus_node; 705 struct ghes_estatus_node *estatus_node;
706 struct acpi_hest_generic *generic;
549 struct acpi_hest_generic_status *estatus; 707 struct acpi_hest_generic_status *estatus;
550 u32 len, node_len; 708 u32 len, node_len;
551 709
@@ -569,7 +727,11 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
569 len = apei_estatus_len(estatus); 727 len = apei_estatus_len(estatus);
570 node_len = GHES_ESTATUS_NODE_LEN(len); 728 node_len = GHES_ESTATUS_NODE_LEN(len);
571 ghes_do_proc(estatus); 729 ghes_do_proc(estatus);
572 ghes_print_estatus(NULL, estatus_node->generic, estatus); 730 if (!ghes_estatus_cached(estatus)) {
731 generic = estatus_node->generic;
732 if (ghes_print_estatus(NULL, generic, estatus))
733 ghes_estatus_cache_add(generic, estatus);
734 }
573 gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, 735 gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
574 node_len); 736 node_len);
575 llnode = next; 737 llnode = next;
@@ -622,6 +784,8 @@ static int ghes_notify_nmi(struct notifier_block *this,
622 if (!(ghes->flags & GHES_TO_CLEAR)) 784 if (!(ghes->flags & GHES_TO_CLEAR))
623 continue; 785 continue;
624#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG 786#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
787 if (ghes_estatus_cached(ghes->estatus))
788 goto next;
625 /* Save estatus for further processing in IRQ context */ 789 /* Save estatus for further processing in IRQ context */
626 len = apei_estatus_len(ghes->estatus); 790 len = apei_estatus_len(ghes->estatus);
627 node_len = GHES_ESTATUS_NODE_LEN(len); 791 node_len = GHES_ESTATUS_NODE_LEN(len);
@@ -633,6 +797,7 @@ static int ghes_notify_nmi(struct notifier_block *this,
633 memcpy(estatus, ghes->estatus, len); 797 memcpy(estatus, ghes->estatus, len);
634 llist_add(&estatus_node->llnode, &ghes_estatus_llist); 798 llist_add(&estatus_node->llnode, &ghes_estatus_llist);
635 } 799 }
800next:
636#endif 801#endif
637 ghes_clear_estatus(ghes); 802 ghes_clear_estatus(ghes);
638 } 803 }
@@ -847,6 +1012,11 @@ static int __init ghes_init(void)
847 if (rc) 1012 if (rc)
848 goto err_ioremap_exit; 1013 goto err_ioremap_exit;
849 1014
1015 rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE *
1016 GHES_ESTATUS_CACHE_ALLOCED_MAX);
1017 if (rc)
1018 goto err_pool_exit;
1019
850 rc = platform_driver_register(&ghes_platform_driver); 1020 rc = platform_driver_register(&ghes_platform_driver);
851 if (rc) 1021 if (rc)
852 goto err_pool_exit; 1022 goto err_pool_exit;