aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/apei/erst.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/apei/erst.c')
-rw-r--r--drivers/acpi/apei/erst.c235
1 files changed, 191 insertions, 44 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index c02005abce43..d6cb0ff6988e 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -430,6 +430,22 @@ ssize_t erst_get_record_count(void)
430} 430}
431EXPORT_SYMBOL_GPL(erst_get_record_count); 431EXPORT_SYMBOL_GPL(erst_get_record_count);
432 432
433#define ERST_RECORD_ID_CACHE_SIZE_MIN 16
434#define ERST_RECORD_ID_CACHE_SIZE_MAX 1024
435
436struct erst_record_id_cache {
437 struct mutex lock;
438 u64 *entries;
439 int len;
440 int size;
441 int refcount;
442};
443
444static struct erst_record_id_cache erst_record_id_cache = {
445 .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock),
446 .refcount = 0,
447};
448
433static int __erst_get_next_record_id(u64 *record_id) 449static int __erst_get_next_record_id(u64 *record_id)
434{ 450{
435 struct apei_exec_context ctx; 451 struct apei_exec_context ctx;
@@ -444,26 +460,179 @@ static int __erst_get_next_record_id(u64 *record_id)
444 return 0; 460 return 0;
445} 461}
446 462
463int erst_get_record_id_begin(int *pos)
464{
465 int rc;
466
467 if (erst_disable)
468 return -ENODEV;
469
470 rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
471 if (rc)
472 return rc;
473 erst_record_id_cache.refcount++;
474 mutex_unlock(&erst_record_id_cache.lock);
475
476 *pos = 0;
477
478 return 0;
479}
480EXPORT_SYMBOL_GPL(erst_get_record_id_begin);
481
482/* erst_record_id_cache.lock must be held by caller */
483static int __erst_record_id_cache_add_one(void)
484{
485 u64 id, prev_id, first_id;
486 int i, rc;
487 u64 *entries;
488 unsigned long flags;
489
490 id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID;
491retry:
492 raw_spin_lock_irqsave(&erst_lock, flags);
493 rc = __erst_get_next_record_id(&id);
494 raw_spin_unlock_irqrestore(&erst_lock, flags);
495 if (rc == -ENOENT)
496 return 0;
497 if (rc)
498 return rc;
499 if (id == APEI_ERST_INVALID_RECORD_ID)
500 return 0;
501 /* can not skip current ID, or loop back to first ID */
502 if (id == prev_id || id == first_id)
503 return 0;
504 if (first_id == APEI_ERST_INVALID_RECORD_ID)
505 first_id = id;
506 prev_id = id;
507
508 entries = erst_record_id_cache.entries;
509 for (i = 0; i < erst_record_id_cache.len; i++) {
510 if (entries[i] == id)
511 break;
512 }
513 /* record id already in cache, try next */
514 if (i < erst_record_id_cache.len)
515 goto retry;
516 if (erst_record_id_cache.len >= erst_record_id_cache.size) {
517 int new_size, alloc_size;
518 u64 *new_entries;
519
520 new_size = erst_record_id_cache.size * 2;
521 new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN,
522 ERST_RECORD_ID_CACHE_SIZE_MAX);
523 if (new_size <= erst_record_id_cache.size) {
524 if (printk_ratelimit())
525 pr_warning(FW_WARN ERST_PFX
526 "too many record ID!\n");
527 return 0;
528 }
529 alloc_size = new_size * sizeof(entries[0]);
530 if (alloc_size < PAGE_SIZE)
531 new_entries = kmalloc(alloc_size, GFP_KERNEL);
532 else
533 new_entries = vmalloc(alloc_size);
534 if (!new_entries)
535 return -ENOMEM;
536 memcpy(new_entries, entries,
537 erst_record_id_cache.len * sizeof(entries[0]));
538 if (erst_record_id_cache.size < PAGE_SIZE)
539 kfree(entries);
540 else
541 vfree(entries);
542 erst_record_id_cache.entries = entries = new_entries;
543 erst_record_id_cache.size = new_size;
544 }
545 entries[i] = id;
546 erst_record_id_cache.len++;
547
548 return 1;
549}
550
447/* 551/*
448 * Get the record ID of an existing error record on the persistent 552 * Get the record ID of an existing error record on the persistent
449 * storage. If there is no error record on the persistent storage, the 553 * storage. If there is no error record on the persistent storage, the
450 * returned record_id is APEI_ERST_INVALID_RECORD_ID. 554 * returned record_id is APEI_ERST_INVALID_RECORD_ID.
451 */ 555 */
452int erst_get_next_record_id(u64 *record_id) 556int erst_get_record_id_next(int *pos, u64 *record_id)
453{ 557{
454 int rc; 558 int rc = 0;
455 unsigned long flags; 559 u64 *entries;
456 560
457 if (erst_disable) 561 if (erst_disable)
458 return -ENODEV; 562 return -ENODEV;
459 563
460 raw_spin_lock_irqsave(&erst_lock, flags); 564 /* must be enclosed by erst_get_record_id_begin/end */
461 rc = __erst_get_next_record_id(record_id); 565 BUG_ON(!erst_record_id_cache.refcount);
462 raw_spin_unlock_irqrestore(&erst_lock, flags); 566 BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len);
567
568 mutex_lock(&erst_record_id_cache.lock);
569 entries = erst_record_id_cache.entries;
570 for (; *pos < erst_record_id_cache.len; (*pos)++)
571 if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID)
572 break;
573 /* found next record id in cache */
574 if (*pos < erst_record_id_cache.len) {
575 *record_id = entries[*pos];
576 (*pos)++;
577 goto out_unlock;
578 }
579
580 /* Try to add one more record ID to cache */
581 rc = __erst_record_id_cache_add_one();
582 if (rc < 0)
583 goto out_unlock;
584 /* successfully add one new ID */
585 if (rc == 1) {
586 *record_id = erst_record_id_cache.entries[*pos];
587 (*pos)++;
588 rc = 0;
589 } else {
590 *pos = -1;
591 *record_id = APEI_ERST_INVALID_RECORD_ID;
592 }
593out_unlock:
594 mutex_unlock(&erst_record_id_cache.lock);
463 595
464 return rc; 596 return rc;
465} 597}
466EXPORT_SYMBOL_GPL(erst_get_next_record_id); 598EXPORT_SYMBOL_GPL(erst_get_record_id_next);
599
600/* erst_record_id_cache.lock must be held by caller */
601static void __erst_record_id_cache_compact(void)
602{
603 int i, wpos = 0;
604 u64 *entries;
605
606 if (erst_record_id_cache.refcount)
607 return;
608
609 entries = erst_record_id_cache.entries;
610 for (i = 0; i < erst_record_id_cache.len; i++) {
611 if (entries[i] == APEI_ERST_INVALID_RECORD_ID)
612 continue;
613 if (wpos != i)
614 memcpy(&entries[wpos], &entries[i], sizeof(entries[i]));
615 wpos++;
616 }
617 erst_record_id_cache.len = wpos;
618}
619
620void erst_get_record_id_end(void)
621{
622 /*
623 * erst_disable != 0 should be detected by invoker via the
624 * return value of erst_get_record_id_begin/next, so this
625 * function should not be called for erst_disable != 0.
626 */
627 BUG_ON(erst_disable);
628
629 mutex_lock(&erst_record_id_cache.lock);
630 erst_record_id_cache.refcount--;
631 BUG_ON(erst_record_id_cache.refcount < 0);
632 __erst_record_id_cache_compact();
633 mutex_unlock(&erst_record_id_cache.lock);
634}
635EXPORT_SYMBOL_GPL(erst_get_record_id_end);
467 636
468static int __erst_write_to_storage(u64 offset) 637static int __erst_write_to_storage(u64 offset)
469{ 638{
@@ -704,56 +873,34 @@ ssize_t erst_read(u64 record_id, struct cper_record_header *record,
704} 873}
705EXPORT_SYMBOL_GPL(erst_read); 874EXPORT_SYMBOL_GPL(erst_read);
706 875
707/*
708 * If return value > buflen, the buffer size is not big enough,
709 * else if return value = 0, there is no more record to read,
710 * else if return value < 0, something goes wrong,
711 * else everything is OK, and return value is record length
712 */
713ssize_t erst_read_next(struct cper_record_header *record, size_t buflen)
714{
715 int rc;
716 ssize_t len;
717 unsigned long flags;
718 u64 record_id;
719
720 if (erst_disable)
721 return -ENODEV;
722
723 raw_spin_lock_irqsave(&erst_lock, flags);
724 rc = __erst_get_next_record_id(&record_id);
725 if (rc) {
726 raw_spin_unlock_irqrestore(&erst_lock, flags);
727 return rc;
728 }
729 /* no more record */
730 if (record_id == APEI_ERST_INVALID_RECORD_ID) {
731 raw_spin_unlock_irqrestore(&erst_lock, flags);
732 return 0;
733 }
734
735 len = __erst_read(record_id, record, buflen);
736 raw_spin_unlock_irqrestore(&erst_lock, flags);
737
738 return len;
739}
740EXPORT_SYMBOL_GPL(erst_read_next);
741
742int erst_clear(u64 record_id) 876int erst_clear(u64 record_id)
743{ 877{
744 int rc; 878 int rc, i;
745 unsigned long flags; 879 unsigned long flags;
880 u64 *entries;
746 881
747 if (erst_disable) 882 if (erst_disable)
748 return -ENODEV; 883 return -ENODEV;
749 884
885 rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
886 if (rc)
887 return rc;
750 raw_spin_lock_irqsave(&erst_lock, flags); 888 raw_spin_lock_irqsave(&erst_lock, flags);
751 if (erst_erange.attr & ERST_RANGE_NVRAM) 889 if (erst_erange.attr & ERST_RANGE_NVRAM)
752 rc = __erst_clear_from_nvram(record_id); 890 rc = __erst_clear_from_nvram(record_id);
753 else 891 else
754 rc = __erst_clear_from_storage(record_id); 892 rc = __erst_clear_from_storage(record_id);
755 raw_spin_unlock_irqrestore(&erst_lock, flags); 893 raw_spin_unlock_irqrestore(&erst_lock, flags);
756 894 if (rc)
895 goto out;
896 entries = erst_record_id_cache.entries;
897 for (i = 0; i < erst_record_id_cache.len; i++) {
898 if (entries[i] == record_id)
899 entries[i] = APEI_ERST_INVALID_RECORD_ID;
900 }
901 __erst_record_id_cache_compact();
902out:
903 mutex_unlock(&erst_record_id_cache.lock);
757 return rc; 904 return rc;
758} 905}
759EXPORT_SYMBOL_GPL(erst_clear); 906EXPORT_SYMBOL_GPL(erst_clear);