~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to drivers/acpi/apei/erst.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
#include <linux/cper.h>
35
35
#include <linux/nmi.h>
36
36
#include <linux/hardirq.h>
 
37
#include <linux/pstore.h>
37
38
#include <acpi/apei.h>
38
39
 
39
40
#include "apei-internal.h"
429
430
}
430
431
EXPORT_SYMBOL_GPL(erst_get_record_count);
431
432
 
 
433
#define ERST_RECORD_ID_CACHE_SIZE_MIN   16
 
434
#define ERST_RECORD_ID_CACHE_SIZE_MAX   1024
 
435
 
 
436
struct erst_record_id_cache {
 
437
        struct mutex lock;
 
438
        u64 *entries;
 
439
        int len;
 
440
        int size;
 
441
        int refcount;
 
442
};
 
443
 
 
444
static struct erst_record_id_cache erst_record_id_cache = {
 
445
        .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock),
 
446
        .refcount = 0,
 
447
};
 
448
 
432
449
static int __erst_get_next_record_id(u64 *record_id)
433
450
{
434
451
        struct apei_exec_context ctx;
443
460
        return 0;
444
461
}
445
462
 
 
463
int 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
}
 
480
EXPORT_SYMBOL_GPL(erst_get_record_id_begin);
 
481
 
 
482
/* erst_record_id_cache.lock must be held by caller */
 
483
static 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;
 
491
retry:
 
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
 
446
551
/*
447
552
 * Get the record ID of an existing error record on the persistent
448
553
 * storage. If there is no error record on the persistent storage, the
449
554
 * returned record_id is APEI_ERST_INVALID_RECORD_ID.
450
555
 */
451
 
int erst_get_next_record_id(u64 *record_id)
 
556
int erst_get_record_id_next(int *pos, u64 *record_id)
452
557
{
453
 
        int rc;
454
 
        unsigned long flags;
 
558
        int rc = 0;
 
559
        u64 *entries;
455
560
 
456
561
        if (erst_disable)
457
562
                return -ENODEV;
458
563
 
459
 
        raw_spin_lock_irqsave(&erst_lock, flags);
460
 
        rc = __erst_get_next_record_id(record_id);
461
 
        raw_spin_unlock_irqrestore(&erst_lock, flags);
 
564
        /* must be enclosed by erst_get_record_id_begin/end */
 
565
        BUG_ON(!erst_record_id_cache.refcount);
 
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
        }
 
593
out_unlock:
 
594
        mutex_unlock(&erst_record_id_cache.lock);
462
595
 
463
596
        return rc;
464
597
}
465
 
EXPORT_SYMBOL_GPL(erst_get_next_record_id);
 
598
EXPORT_SYMBOL_GPL(erst_get_record_id_next);
 
599
 
 
600
/* erst_record_id_cache.lock must be held by caller */
 
601
static 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
 
 
620
void 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
}
 
635
EXPORT_SYMBOL_GPL(erst_get_record_id_end);
466
636
 
467
637
static int __erst_write_to_storage(u64 offset)
468
638
{
703
873
}
704
874
EXPORT_SYMBOL_GPL(erst_read);
705
875
 
706
 
/*
707
 
 * If return value > buflen, the buffer size is not big enough,
708
 
 * else if return value = 0, there is no more record to read,
709
 
 * else if return value < 0, something goes wrong,
710
 
 * else everything is OK, and return value is record length
711
 
 */
712
 
ssize_t erst_read_next(struct cper_record_header *record, size_t buflen)
713
 
{
714
 
        int rc;
715
 
        ssize_t len;
716
 
        unsigned long flags;
717
 
        u64 record_id;
718
 
 
719
 
        if (erst_disable)
720
 
                return -ENODEV;
721
 
 
722
 
        raw_spin_lock_irqsave(&erst_lock, flags);
723
 
        rc = __erst_get_next_record_id(&record_id);
724
 
        if (rc) {
725
 
                raw_spin_unlock_irqrestore(&erst_lock, flags);
726
 
                return rc;
727
 
        }
728
 
        /* no more record */
729
 
        if (record_id == APEI_ERST_INVALID_RECORD_ID) {
730
 
                raw_spin_unlock_irqrestore(&erst_lock, flags);
731
 
                return 0;
732
 
        }
733
 
 
734
 
        len = __erst_read(record_id, record, buflen);
735
 
        raw_spin_unlock_irqrestore(&erst_lock, flags);
736
 
 
737
 
        return len;
738
 
}
739
 
EXPORT_SYMBOL_GPL(erst_read_next);
740
 
 
741
876
int erst_clear(u64 record_id)
742
877
{
743
 
        int rc;
 
878
        int rc, i;
744
879
        unsigned long flags;
 
880
        u64 *entries;
745
881
 
746
882
        if (erst_disable)
747
883
                return -ENODEV;
748
884
 
 
885
        rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
 
886
        if (rc)
 
887
                return rc;
749
888
        raw_spin_lock_irqsave(&erst_lock, flags);
750
889
        if (erst_erange.attr & ERST_RANGE_NVRAM)
751
890
                rc = __erst_clear_from_nvram(record_id);
752
891
        else
753
892
                rc = __erst_clear_from_storage(record_id);
754
893
        raw_spin_unlock_irqrestore(&erst_lock, flags);
755
 
 
 
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();
 
902
out:
 
903
        mutex_unlock(&erst_record_id_cache.lock);
756
904
        return rc;
757
905
}
758
906
EXPORT_SYMBOL_GPL(erst_clear);
781
929
        return 0;
782
930
}
783
931
 
 
932
static int erst_open_pstore(struct pstore_info *psi);
 
933
static int erst_close_pstore(struct pstore_info *psi);
 
934
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
 
935
                       struct timespec *time);
 
936
static u64 erst_writer(enum pstore_type_id type, size_t size);
 
937
 
 
938
static struct pstore_info erst_info = {
 
939
        .owner          = THIS_MODULE,
 
940
        .name           = "erst",
 
941
        .open           = erst_open_pstore,
 
942
        .close          = erst_close_pstore,
 
943
        .read           = erst_reader,
 
944
        .write          = erst_writer,
 
945
        .erase          = erst_clear
 
946
};
 
947
 
 
948
#define CPER_CREATOR_PSTORE                                             \
 
949
        UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c,     \
 
950
                0x64, 0x90, 0xb8, 0x9d)
 
951
#define CPER_SECTION_TYPE_DMESG                                         \
 
952
        UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54,     \
 
953
                0x94, 0x19, 0xeb, 0x12)
 
954
#define CPER_SECTION_TYPE_MCE                                           \
 
955
        UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96,     \
 
956
                0x04, 0x4a, 0x38, 0xfc)
 
957
 
 
958
struct cper_pstore_record {
 
959
        struct cper_record_header hdr;
 
960
        struct cper_section_descriptor sec_hdr;
 
961
        char data[];
 
962
} __packed;
 
963
 
 
964
static int reader_pos;
 
965
 
 
966
static int erst_open_pstore(struct pstore_info *psi)
 
967
{
 
968
        int rc;
 
969
 
 
970
        if (erst_disable)
 
971
                return -ENODEV;
 
972
 
 
973
        rc = erst_get_record_id_begin(&reader_pos);
 
974
 
 
975
        return rc;
 
976
}
 
977
 
 
978
static int erst_close_pstore(struct pstore_info *psi)
 
979
{
 
980
        erst_get_record_id_end();
 
981
 
 
982
        return 0;
 
983
}
 
984
 
 
985
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
 
986
                       struct timespec *time)
 
987
{
 
988
        int rc;
 
989
        ssize_t len = 0;
 
990
        u64 record_id;
 
991
        struct cper_pstore_record *rcd = (struct cper_pstore_record *)
 
992
                                        (erst_info.buf - sizeof(*rcd));
 
993
 
 
994
        if (erst_disable)
 
995
                return -ENODEV;
 
996
 
 
997
skip:
 
998
        rc = erst_get_record_id_next(&reader_pos, &record_id);
 
999
        if (rc)
 
1000
                goto out;
 
1001
 
 
1002
        /* no more record */
 
1003
        if (record_id == APEI_ERST_INVALID_RECORD_ID) {
 
1004
                rc = -1;
 
1005
                goto out;
 
1006
        }
 
1007
 
 
1008
        len = erst_read(record_id, &rcd->hdr, sizeof(*rcd) +
 
1009
                        erst_info.bufsize);
 
1010
        /* The record may be cleared by others, try read next record */
 
1011
        if (len == -ENOENT)
 
1012
                goto skip;
 
1013
        else if (len < 0) {
 
1014
                rc = -1;
 
1015
                goto out;
 
1016
        }
 
1017
        if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
 
1018
                goto skip;
 
1019
 
 
1020
        *id = record_id;
 
1021
        if (uuid_le_cmp(rcd->sec_hdr.section_type,
 
1022
                        CPER_SECTION_TYPE_DMESG) == 0)
 
1023
                *type = PSTORE_TYPE_DMESG;
 
1024
        else if (uuid_le_cmp(rcd->sec_hdr.section_type,
 
1025
                             CPER_SECTION_TYPE_MCE) == 0)
 
1026
                *type = PSTORE_TYPE_MCE;
 
1027
        else
 
1028
                *type = PSTORE_TYPE_UNKNOWN;
 
1029
 
 
1030
        if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
 
1031
                time->tv_sec = rcd->hdr.timestamp;
 
1032
        else
 
1033
                time->tv_sec = 0;
 
1034
        time->tv_nsec = 0;
 
1035
 
 
1036
out:
 
1037
        return (rc < 0) ? rc : (len - sizeof(*rcd));
 
1038
}
 
1039
 
 
1040
static u64 erst_writer(enum pstore_type_id type, size_t size)
 
1041
{
 
1042
        struct cper_pstore_record *rcd = (struct cper_pstore_record *)
 
1043
                                        (erst_info.buf - sizeof(*rcd));
 
1044
 
 
1045
        memset(rcd, 0, sizeof(*rcd));
 
1046
        memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
 
1047
        rcd->hdr.revision = CPER_RECORD_REV;
 
1048
        rcd->hdr.signature_end = CPER_SIG_END;
 
1049
        rcd->hdr.section_count = 1;
 
1050
        rcd->hdr.error_severity = CPER_SEV_FATAL;
 
1051
        /* timestamp valid. platform_id, partition_id are invalid */
 
1052
        rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
 
1053
        rcd->hdr.timestamp = get_seconds();
 
1054
        rcd->hdr.record_length = sizeof(*rcd) + size;
 
1055
        rcd->hdr.creator_id = CPER_CREATOR_PSTORE;
 
1056
        rcd->hdr.notification_type = CPER_NOTIFY_MCE;
 
1057
        rcd->hdr.record_id = cper_next_record_id();
 
1058
        rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
 
1059
 
 
1060
        rcd->sec_hdr.section_offset = sizeof(*rcd);
 
1061
        rcd->sec_hdr.section_length = size;
 
1062
        rcd->sec_hdr.revision = CPER_SEC_REV;
 
1063
        /* fru_id and fru_text is invalid */
 
1064
        rcd->sec_hdr.validation_bits = 0;
 
1065
        rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
 
1066
        switch (type) {
 
1067
        case PSTORE_TYPE_DMESG:
 
1068
                rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
 
1069
                break;
 
1070
        case PSTORE_TYPE_MCE:
 
1071
                rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
 
1072
                break;
 
1073
        default:
 
1074
                return -EINVAL;
 
1075
        }
 
1076
        rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
 
1077
 
 
1078
        erst_write(&rcd->hdr);
 
1079
 
 
1080
        return rcd->hdr.record_id;
 
1081
}
 
1082
 
784
1083
static int __init erst_init(void)
785
1084
{
786
1085
        int rc = 0;
788
1087
        struct apei_exec_context ctx;
789
1088
        struct apei_resources erst_resources;
790
1089
        struct resource *r;
 
1090
        char *buf;
791
1091
 
792
1092
        if (acpi_disabled)
793
1093
                goto err;
854
1154
        if (!erst_erange.vaddr)
855
1155
                goto err_release_erange;
856
1156
 
 
1157
        buf = kmalloc(erst_erange.size, GFP_KERNEL);
 
1158
        mutex_init(&erst_info.buf_mutex);
 
1159
        if (buf) {
 
1160
                erst_info.buf = buf + sizeof(struct cper_pstore_record);
 
1161
                erst_info.bufsize = erst_erange.size -
 
1162
                                    sizeof(struct cper_pstore_record);
 
1163
                if (pstore_register(&erst_info)) {
 
1164
                        pr_info(ERST_PFX "Could not register with persistent store\n");
 
1165
                        kfree(buf);
 
1166
                }
 
1167
        }
 
1168
 
857
1169
        pr_info(ERST_PFX
858
1170
        "Error Record Serialization Table (ERST) support is initialized.\n");
859
1171