mce.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * NFIT - Machine Check Handler
  4. *
  5. * Copyright(c) 2013-2016 Intel Corporation. All rights reserved.
  6. */
  7. #include <linux/notifier.h>
  8. #include <linux/acpi.h>
  9. #include <linux/nd.h>
  10. #include <asm/mce.h>
  11. #include "nfit.h"
  12. static int nfit_handle_mce(struct notifier_block *nb, unsigned long val,
  13. void *data)
  14. {
  15. struct mce *mce = (struct mce *)data;
  16. struct acpi_nfit_desc *acpi_desc;
  17. struct nfit_spa *nfit_spa;
  18. /* We only care about uncorrectable memory errors */
  19. if (!mce_is_memory_error(mce) || mce_is_correctable(mce))
  20. return NOTIFY_DONE;
  21. /* Verify the address reported in the MCE is valid. */
  22. if (!mce_usable_address(mce))
  23. return NOTIFY_DONE;
  24. /*
  25. * mce->addr contains the physical addr accessed that caused the
  26. * machine check. We need to walk through the list of NFITs, and see
  27. * if any of them matches that address, and only then start a scrub.
  28. */
  29. mutex_lock(&acpi_desc_lock);
  30. list_for_each_entry(acpi_desc, &acpi_descs, list) {
  31. struct device *dev = acpi_desc->dev;
  32. int found_match = 0;
  33. mutex_lock(&acpi_desc->init_mutex);
  34. list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
  35. struct acpi_nfit_system_address *spa = nfit_spa->spa;
  36. if (nfit_spa_type(spa) != NFIT_SPA_PM)
  37. continue;
  38. /* find the spa that covers the mce addr */
  39. if (spa->address > mce->addr)
  40. continue;
  41. if ((spa->address + spa->length - 1) < mce->addr)
  42. continue;
  43. found_match = 1;
  44. dev_dbg(dev, "addr in SPA %d (0x%llx, 0x%llx)\n",
  45. spa->range_index, spa->address, spa->length);
  46. /*
  47. * We can break at the first match because we're going
  48. * to rescan all the SPA ranges. There shouldn't be any
  49. * aliasing anyway.
  50. */
  51. break;
  52. }
  53. mutex_unlock(&acpi_desc->init_mutex);
  54. if (!found_match)
  55. continue;
  56. /* If this fails due to an -ENOMEM, there is little we can do */
  57. nvdimm_bus_add_badrange(acpi_desc->nvdimm_bus,
  58. ALIGN(mce->addr, L1_CACHE_BYTES),
  59. L1_CACHE_BYTES);
  60. nvdimm_region_notify(nfit_spa->nd_region,
  61. NVDIMM_REVALIDATE_POISON);
  62. if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) {
  63. /*
  64. * We can ignore an -EBUSY here because if an ARS is
  65. * already in progress, just let that be the last
  66. * authoritative one
  67. */
  68. acpi_nfit_ars_rescan(acpi_desc, 0);
  69. }
  70. mce->kflags |= MCE_HANDLED_NFIT;
  71. break;
  72. }
  73. mutex_unlock(&acpi_desc_lock);
  74. return NOTIFY_DONE;
  75. }
  76. static struct notifier_block nfit_mce_dec = {
  77. .notifier_call = nfit_handle_mce,
  78. .priority = MCE_PRIO_NFIT,
  79. };
  80. void nfit_mce_register(void)
  81. {
  82. mce_register_decode_chain(&nfit_mce_dec);
  83. }
  84. void nfit_mce_unregister(void)
  85. {
  86. mce_unregister_decode_chain(&nfit_mce_dec);
  87. }