*/
#include <stdio.h>
+#include <string.h>
#include "ras-mce-handler.h"
#define K8_MCE_THRESHOLD_BASE (MCE_EXTENDED_BANK + 1) /* MCE_AMD */
#define K8_MCE_THRESHOLD_TOP (K8_MCE_THRESHOLD_BASE + 6 * 9)
+
#define K8_MCELOG_THRESHOLD_DRAM_ECC (4 * 9 + 0)
#define K8_MCELOG_THRESHOLD_LINK (4 * 9 + 1)
#define K8_MCELOG_THRESHOLD_L3_CACHE (4 * 9 + 2)
[0] = "err cpu0",
};
-static void decode_k8_generic_errcode(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e)
+#define IGNORE_HIGHBITS ((1 << 31) || (1 << 28) || (1 << 26))
+
+static void decode_k8_generic_errcode(uint64_t status, char *buf, size_t *len)
{
- uint64_t status = e->status;
+ char tmp_buf[4096];
unsigned short errcode = status & 0xffff;
- int i;
-
- for (i = 0; i < 32; i++) {
- if (i == 31 || i == 28 || i == 26)
- continue;
- if (highbits[i] && (status & (1ULL << (i + 32)))) {
- trace_seq_printf(s, " bit%d = %s\n", i + 32, highbits[i]);
- }
+ int i, n;
+ char *p = buf;
+
+ /* Translate the highest bits */
+ n = bitfield_msg(tmp_buf, sizeof(*len), highbits, 32, IGNORE_HIGHBITS,
+ 32, status);
+ if (n) {
+ n = snprintf(p, *len, "%s ", tmp_buf);
+ p += n;
+ *len -= n;
}
if ((errcode & 0xfff0) == 0x0010) {
- trace_seq_printf(s, " TLB error '%s transaction, level %s'\n",
+ n = snprintf(p, *len, "LB error '%s transaction, level %s'",
transaction[(errcode >> 2) & 3],
cachelevel[errcode & 3]);
+ p += n;
+ *len -= n;
}
else if ((errcode & 0xff00) == 0x0100) {
- trace_seq_printf(s, " memory/cache error '%s mem transaction, %s transaction, level %s'\n",
- memtrans[(errcode >> 4) & 0xf],
- transaction[(errcode >> 2) & 3],
- cachelevel[errcode & 3]);
+ n = snprintf(p, *len,
+ "memory/cache error '%s mem transaction, %s transaction, level %s'",
+ memtrans[(errcode >> 4) & 0xf],
+ transaction[(errcode >> 2) & 3],
+ cachelevel[errcode & 3]);
+ p += n;
+ *len -= n;
}
else if ((errcode & 0xf800) == 0x0800) {
- trace_seq_printf(s, " bus error '%s, %s\n %s mem transaction\n %s access, level %s'\n",
- partproc[(errcode >> 9) & 0x3],
- timeout[(errcode >> 8) & 1],
- memtrans[(errcode >> 4) & 0xf],
- memoryio[(errcode >> 2) & 0x3],
- cachelevel[(errcode & 0x3)]);
+ n = snprintf(p, *len,
+ "bus error '%s, %s: %s mem transaction, %s access, level %s'",
+ partproc[(errcode >> 9) & 0x3],
+ timeout[(errcode >> 8) & 1],
+ memtrans[(errcode >> 4) & 0xf],
+ memoryio[(errcode >> 2) & 0x3],
+ cachelevel[(errcode & 0x3)]);
+ p += n;
+ *len -= n;
}
}
-static void decode_k8_dc_mc(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e)
+static void decode_k8_dc_mc(uint64_t status, char *buf, size_t *len)
{
- uint64_t status = e->status;
unsigned short exterrcode = (status >> 16) & 0x0f;
unsigned short errcode = status & 0xffff;
+ int n;
+ char *p = buf;
- if(status & (3ULL << 45)) {
- trace_seq_printf(s, " Data cache ECC error (syndrome %x)",
+ if (status & (3ULL << 45)) {
+ n = snprintf(p, *len, "Data cache ECC error (syndrome %x)",
(uint32_t) (status >> 47) & 0xff);
- if(status&(1ULL << 40)) {
- trace_seq_printf(s, " found by scrubber");
+ p += n;
+ *len -= n;
+ if(status & (1ULL << 40)) {
+ n = snprintf(p, *len, " found by scrubber");
+ p += n;
+ *len -= n;
}
- trace_seq_printf(s, "\n");
}
if ((errcode & 0xfff0) == 0x0010) {
- trace_seq_printf(s, " TLB parity error in %s array\n",
+ if (p != buf) {
+ n = snprintf(p, *len, " ");
+ p += n;
+ *len -= n;
+ }
+ n = snprintf(p, *len, "TLB parity error in %s array",
(exterrcode == 0) ? "physical" : "virtual");
+ p += n;
+ *len -= n;
+ }
+
+ if (p != buf) {
+ n = snprintf(p, *len, " ");
+ p += n;
+ *len -= n;
}
- decode_k8_generic_errcode(ras, s, e);
+ decode_k8_generic_errcode(status, p, len);
}
-static void decode_k8_ic_mc(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e)
+static void decode_k8_ic_mc(uint64_t status, char *buf, size_t *len)
{
- uint64_t status = e->status;
unsigned short exterrcode = (status >> 16) & 0x0f;
unsigned short errcode = status & 0xffff;
+ int n;
+ char *p = buf;
- if(status & (3ULL << 45)) {
- trace_seq_printf(s, " Instruction cache ECC error\n");
+ if (status & (3ULL << 45)) {
+ n = snprintf(p, *len, "Instruction cache ECC error");
+ p += n;
+ *len -= n;
}
if ((errcode & 0xfff0) == 0x0010) {
- trace_seq_printf(s, " TLB parity error in %s array\n",
+ if (p != buf) {
+ n = snprintf(p, *len, " ");
+ p += n;
+ *len -= n;
+ }
+ n = snprintf(p, *len, "TLB parity error in %s array",
(exterrcode == 0) ? "physical" : "virtual");
+ p += n;
+ *len -= n;
+ }
+ if (p != buf) {
+ n = snprintf(p, *len, " ");
+ p += n;
+ *len -= n;
}
- decode_k8_generic_errcode(ras, s, e);
+ decode_k8_generic_errcode(status, p, len);
}
-static void decode_k8_bu_mc(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e)
+static void decode_k8_bu_mc(uint64_t status, char *buf, size_t *len)
{
- uint64_t status = e->status;
unsigned short exterrcode = (status >> 16) & 0x0f;
+ int n;
+ char *p = buf;
+
+ if (status & (3ULL << 45)) {
+ n = snprintf(p, *len, "L2 cache ECC error");
+ p += n;
+ *len -= n;
+ }
- if(status & (3ULL << 45)) {
- trace_seq_printf(s, " L2 cache ECC error\n");
+ if (p != buf) {
+ n = snprintf(p, *len, " ");
+ p += n;
+ *len -= n;
}
- trace_seq_printf(s, " %s array error\n",
- (exterrcode == 0) ? "Bus or cache" : "Cache tag");
+ n = snprintf(p, *len, "%s array error",
+ !exterrcode ? "Bus or cache" : "Cache tag");
- decode_k8_generic_errcode(ras, s, e);
-}
+ if (p != buf) {
+ n = snprintf(p, *len, " ");
+ p += n;
+ *len -= n;
+ }
-static void decode_k8_ls_mc(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e)
-{
- decode_k8_generic_errcode(ras, s, e);
+ decode_k8_generic_errcode(status, p, len);
}
-static void decode_k8_nb_mc(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e,
+static void decode_k8_nb_mc(uint64_t status, char *buf, size_t *len,
unsigned *memerr)
{
- uint64_t status = e->status;
unsigned short exterrcode = (status >> 16) & 0x0f;
+ int n;
+ char *p = buf;
- trace_seq_printf(s, " Northbridge %s\n", nbextendederr[exterrcode]);
+ n = snprintf(buf, *len, "Northbridge %s", nbextendederr[exterrcode]);
+ p += n;
+ *len -= n;
+ n = 0;
switch (exterrcode) {
case 0:
*memerr = 1;
- trace_seq_printf(s, " ECC syndrome = %x\n",
- (uint32_t) (status >> 47) & 0xff);
+ n = snprintf(p, *len, " ECC syndrome = %x",
+ (uint32_t) (status >> 47) & 0xff);
break;
case 8:
*memerr = 1;
- trace_seq_printf(s, " Chipkill ECC syndrome = %x\n",
- (uint32_t) ((((status >> 24) & 0xff) << 8) | ((status >> 47) & 0xff)));
+ n = snprintf(p, *len, " Chipkill ECC syndrome = %x",
+ (uint32_t) ((((status >> 24) & 0xff) << 8) | ((status >> 47) & 0xff)));
break;
case 1:
case 2:
case 3:
case 4:
case 6:
- trace_seq_printf(s, " link number = %x\n",
- (uint32_t) (status >> 36) & 0xf);
+ n = snprintf(p, *len, " link number = %x\n",
+ (uint32_t) (status >> 36) & 0xf);
break;
}
+ p += n;
+ *len -= n;
- decode_k8_generic_errcode(ras, s, e);
-}
+ if (p != buf) {
+ n = snprintf(p, *len, " ");
+ p += n;
+ *len -= n;
+ }
-static void decode_k8_fr_mc(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e)
-{
- decode_k8_generic_errcode(ras, s, e);
+ decode_k8_generic_errcode(status, p, len);
}
-#if 0
-static void decode_k8_threshold(u64 misc)
+static void decode_k8_threashold(uint64_t misc, char *buf, size_t *len)
{
- if (misc & MCI_THRESHOLD_OVER)
- trace_seq_printf(s, " Threshold error count overflow\n");
-}
-#endif
+ int n;
+ char *p = buf;
-static char *bank_name(uint64_t status, unsigned bank)
-{
- static char buf[64];
- char *s = "unknown";
-
- /* Handle GART errors */
- if (bank == 4) {
- unsigned short exterrcode = (status >> 16) & 0x0f;
- if (exterrcode == 5 && (status & (1ULL << 61))) {
- sprintf(buf, "GART error");
- return 0;
- }
+ if (misc & MCI_THRESHOLD_OVER) {
+ n = snprintf(p, *len, " Threshold error count overflow\n");
+ p += n;
+ *len -= n;
}
+}
- if (bank < ARRAY_SIZE(k8bank))
- s = k8bank[bank];
- else if (bank >= K8_MCE_THRESHOLD_BASE &&
- bank < K8_MCE_THRESHOLD_TOP)
- s = k8threshold[bank - K8_MCE_THRESHOLD_BASE];
- else {
- sprintf(buf, "bank=%x", bank);
- return buf;
- }
- snprintf(buf, sizeof(buf) - 1, "%s (bank=%d)", s, bank);
- return buf;
+static void bank_name(struct mce_event *e)
+{
+ char *buf = e->bank_name;
+ char *s;
+
+ if (e->bank < ARRAY_SIZE(k8bank))
+ s = k8bank[e->bank];
+ else if (e->bank >= K8_MCE_THRESHOLD_BASE &&
+ e->bank < K8_MCE_THRESHOLD_TOP)
+ s = k8threshold[e->bank - K8_MCE_THRESHOLD_BASE];
+ else
+ return; /* Use the generic parser for bank */
+
+ snprintf(buf, sizeof(buf) - 1, "%s (bank=%d)", s, e->bank);
}
-void dump_amd_k8_event(struct ras_events *ras,
- struct trace_seq *s, struct mce_event *e)
+int parse_amd_k8_event(struct ras_events *ras, struct mce_event *e)
{
unsigned unknown_bank = 0;
unsigned ismemerr = 0;
+ char *buf = e->error_msg;
+ size_t len = sizeof(e->error_msg);
+
+ /* Don't handle GART errors */
+ if (e->bank == 4) {
+ unsigned short exterrcode = (e->status >> 16) & 0x0f;
+ if (exterrcode == 5 && (e->status & (1ULL << 61))) {
+ return -1;
+ }
+ }
- trace_seq_printf(s, "%s ",bank_name(e->status, e->bank));
+ bank_name(e);
switch (e->bank) {
case 0:
- decode_k8_dc_mc(ras, s, e);
+ decode_k8_dc_mc(e->status, buf, &len);
break;
case 1:
- decode_k8_ic_mc(ras, s, e);
+ decode_k8_ic_mc(e->status, buf, &len);
break;
case 2:
- decode_k8_bu_mc(ras, s, e);
+ decode_k8_bu_mc(e->status, buf, &len);
break;
- case 3:
- decode_k8_ls_mc(ras, s, e);
+ case 3: /* LS */
+ decode_k8_generic_errcode(e->status, buf, &len);
break;
case 4:
- decode_k8_nb_mc(ras, s, e, &ismemerr);
+ decode_k8_nb_mc(e->status, buf, &len, &ismemerr);
+ break;
+ case 5: /* FR */
+ decode_k8_generic_errcode(e->status, buf, &len);
break;
- case 5:
- decode_k8_fr_mc(ras, s, e);
+ case K8_MCE_THRESHOLD_BASE ... K8_MCE_THRESHOLD_TOP:
+ decode_k8_threashold(e->misc, buf, &len);
break;
default:
- trace_seq_printf(s, "Don't know how to decode this bank");
- unknown_bank = 1;
+ strcpy(e->error_msg, "Don't know how to decode this bank");
}
- trace_seq_printf(s, ", mcgcap= %d ", e->mcgcap);
- trace_seq_printf(s, ", mcgstatus= %d ", e->mcgstatus);
- trace_seq_printf(s, ", status= %d ", e->status);
- trace_seq_printf(s, ", addr= %d ", e->addr);
- trace_seq_printf(s, ", misc= %d ", e->misc);
- trace_seq_printf(s, ", ip= %d ", e->ip);
- trace_seq_printf(s, ", tsc= %d ", e->tsc);
- trace_seq_printf(s, ", walltime= %d ", e->walltime);
- trace_seq_printf(s, ", cpu= %d ", e->cpu);
- trace_seq_printf(s, ", cpuid= %d ", e->cpuid);
- trace_seq_printf(s, ", apicid= %d ", e->apicid);
- trace_seq_printf(s, ", socketid= %d ", e->socketid);
- trace_seq_printf(s, ", cs= %d ", e->cs);
- trace_seq_printf(s, ", cpuvendor= %d", e->cpuvendor);
+
+ /* IP doesn't matter on memory errors */
+ if (ismemerr)
+ e->ip = 0;
+
+ return 0;
}
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <stdint.h>
#include "libtrace/kbuffer.h"
#include "ras-mce-handler.h"
#include "ras-record.h"
* End of mcelog's code
*/
-static void dump_mce_event(struct trace_seq *s, struct mce_event *e)
+unsigned bitfield_msg(char *buf, size_t len, char **bitarray, unsigned array_len,
+ unsigned bit_offset, unsigned ignore_bits,
+ uint64_t status)
{
- trace_seq_printf(s, "bank=%s ",e->bank);
- trace_seq_printf(s, ", mcgcap= %d ", e->mcgcap);
- trace_seq_printf(s, ", mcgstatus= %d ", e->mcgstatus);
- trace_seq_printf(s, ", status= %d ", e->status);
- trace_seq_printf(s, ", addr= %d ", e->addr);
- trace_seq_printf(s, ", misc= %d ", e->misc);
- trace_seq_printf(s, ", ip= %d ", e->ip);
- trace_seq_printf(s, ", tsc= %d ", e->tsc);
- trace_seq_printf(s, ", walltime= %d ", e->walltime);
- trace_seq_printf(s, ", cpu= %d ", e->cpu);
- trace_seq_printf(s, ", cpuid= %d ", e->cpuid);
- trace_seq_printf(s, ", apicid= %d ", e->apicid);
- trace_seq_printf(s, ", socketid= %d ", e->socketid);
- trace_seq_printf(s, ", cs= %d ", e->cs);
- trace_seq_printf(s, ", cpuvendor= %d", e->cpuvendor);
-}
+ int i, n;
+ char *p = buf;
+ len--;
-int ras_mce_event_handler(struct trace_seq *s,
- struct pevent_record *record,
- struct event_format *event, void *context)
+ for (i = 0; i < array_len; i++) {
+ if (status & ignore_bits)
+ continue;
+ if (status & (1 << (i + bit_offset))) {
+ if (p != buf) {
+ n = snprintf(p, len, ", ");
+ len -= n;
+ p += n;
+ }
+ if (!bitarray[i])
+ n = snprintf(p, len, "BIT%d", i + bit_offset);
+ else
+ n = snprintf(p, len, "%s", bitarray[i]);
+ len -= n;
+ p += n;
+ }
+ }
+
+ *p = 0;
+ return p - buf;
+}
+
+static void report_mce_event(struct ras_events *ras,
+ struct pevent_record *record,
+ struct trace_seq *s, struct mce_event *e)
{
- int len;
unsigned long long val;
- struct ras_events *ras = context;
time_t now;
struct tm *tm;
- struct ras_aer_event ev;
- char buf[1024];
struct mce_priv *mce = ras->mce_priv;
- struct mce_event e;
/*
* Newer kernels (3.10-rc1 or upper) provide an uptime clock.
tm = localtime(&now);
if (tm)
- strftime(ev.timestamp, sizeof(ev.timestamp),
+ strftime(e->timestamp, sizeof(e->timestamp),
"%Y-%m-%d %H:%M:%S %z", tm);
- trace_seq_printf(s, "%s ", ev.timestamp);
+ trace_seq_printf(s, "%s ", e->timestamp);
+
+ if (*e->bank_name)
+ trace_seq_printf(s, "%s", e->bank_name);
+ else
+ trace_seq_printf(s, "bank=%x", e->bank);
+
+ trace_seq_printf(s, ", status= %d ", e->status);
+ if (*e->error_msg)
+ trace_seq_printf(s, ", %s ", e->error_msg);
+
+#if 0
+ /*
+ * While the logic for decoding tsc is there at mcelog, why to
+ * decode/print it, if we already got the uptime from the
+ * tracing event? Let's just discard it for now.
+ */
+ trace_seq_printf(s, ", tsc= %d ", e->tsc);
+ trace_seq_printf(s, ", walltime= %d ", e->walltime);
+#endif
trace_seq_printf(s, "CPU: %s, ", cputype_name[mce->cputype]);
+ trace_seq_printf(s, ", cpu= %d ", e->cpu);
+ trace_seq_printf(s, ", socketid= %d ", e->socketid);
+
+#if 0
+ /*
+ * The CPU vendor is already reported from mce->cputype
+ */
+ trace_seq_printf(s, ", cpuvendor= %d", e->cpuvendor);
+ trace_seq_printf(s, ", cpuid= %d ", e->cpuid);
+#endif
+
+ if (e->ip)
+ trace_seq_printf(s, ", ip= %d%s ",
+ !(e->mcgstatus & MCG_STATUS_EIPV) ? " (INEXACT)" : "",
+ e->ip);
+
+ if (e->cs)
+ trace_seq_printf(s, ", cs= %d ", e->cs);
+
+ if (e->status & MCI_STATUS_MISCV)
+ trace_seq_printf(s, ", misc= %d ", e->misc);
+
+ if (e->status & MCI_STATUS_ADDRV)
+ trace_seq_printf(s, ", addr= %d ", e->addr);
+
+ trace_seq_printf(s, ", mcgstatus= %d ", e->mcgstatus);
+
+ if (e->mcgcap)
+ trace_seq_printf(s, ", mcgcap= %d ", e->mcgcap);
+
+ trace_seq_printf(s, ", apicid= %d ", e->apicid);
+
+ /*
+ * FIXME: The original mcelog userspace tool uses DMI to map from
+ * address to DIMM. From the comments there, the code there doesn't
+ * take interleaving sets into account. Also, it is known that
+ * BIOS is generally not reliable enough to associate DIMM labels
+ * with addresses.
+ * As, in thesis, we shouldn't be receiving memory error reports via
+ * MCE, as they should go via EDAC traces, let's not do it.
+ */
+}
+
+int ras_mce_event_handler(struct trace_seq *s,
+ struct pevent_record *record,
+ struct event_format *event, void *context)
+{
+ unsigned long long val;
+ struct ras_events *ras = context;
+ struct mce_priv *mce = ras->mce_priv;
+ struct mce_event e;
+ int rc;
+
+ memset(&e, 0, sizeof(e));
/* Parse the MCE error data */
if (pevent_get_field_val(s, event, "mcgcap", record, &val, 1) < 0)
switch (mce->cputype) {
case CPU_GENERIC:
- dump_mce_event(s, &e);
+ break;
case CPU_K8:
- dump_amd_k8_event(ras, s, &e);
+ rc = parse_amd_k8_event(ras, &e);
+ break;
default: /* All other CPU types are Intel */
- dump_intel_event(ras, s, &e);
+ rc = parse_intel_event(ras, &e);
}
- return 0;
+ if (rc)
+ return rc;
+
+ report_mce_event(ras, record, s, &e);
}