]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'edac_for_4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 17 May 2016 01:44:39 +0000 (18:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 17 May 2016 01:44:39 +0000 (18:44 -0700)
Pull EDAC updates from Borislav Petkov:
 "It was pretty busy in EDAC land this time:

   - Altera Arria10 L2 cache and On-Chip RAM ECC handling (Thor Thayer)

   - Remove ad-hoc buffering of MCE records in sb_edac and i7core_edac
     (Tony Luck)

   - Do not register sb_edac with pci_register_driver() (Tony Luck)

   - Add support for Skylake to ie31200_edac (Jason Baron)

   - Do not register amd64_edac with pci_register_driver() (Borislav
     Petkov)

  ... plus the usual round of cleanups and fixes all over the place"

* tag 'edac_for_4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp: (25 commits)
  EDAC, amd64_edac: Drop pci_register_driver() use
  EDAC, ie31200_edac: Add Skylake support
  EDAC, sb_edac: Use cpu family/model in driver detection
  EDAC, i7core: Remove double buffering of error records
  EDAC, amd64_edac: Issue driver banner only on success
  ARM: socfpga: Initialize Arria10 OCRAM ECC on startup
  EDAC: Increment correct counter in edac_inc_ue_error()
  EDAC, sb_edac: Remove double buffering of error records
  EDAC: Fix used after kfree() error in edac_unregister_sysfs()
  EDAC, altera: Avoid unused function warnings
  EDAC, altera: Remove useless casts
  ARM: socfpga: Enable Arria10 OCRAM ECC on startup
  EDAC, altera: Add Arria10 OCRAM ECC support
  Documentation: dt: socfpga: Add Altera Arria10 OCRAM binding
  EDAC, altera: Make OCRAM ECC dependency check generic
  EDAC, altera: Add register offset for ECC Enable
  EDAC, altera: Extract error inject operations to a struct fops
  ARM: socfpga: Enable Arria10 L2 cache ECC on startup
  EDAC, altera: Add Arria10 L2 Cache ECC handling
  Documentation, dt, socfpga: Add Altera Arria10 L2 cache binding
  ...

1  2 
drivers/edac/i7core_edac.c
drivers/edac/sb_edac.c

index 792bdae2b91dfcf28a8a7fca199212d4990aa8af,60e0bb53e9c9eccc0364148e673b7444a46384a9..8a68a5e943ea337b23efc3662f5a01848d0b1a8b
@@@ -271,16 -271,6 +271,6 @@@ struct i7core_pvt 
  
        bool            is_registered, enable_scrub;
  
-       /* Fifo double buffers */
-       struct mce              mce_entry[MCE_LOG_LEN];
-       struct mce              mce_outentry[MCE_LOG_LEN];
-       /* Fifo in/out counters */
-       unsigned                mce_in, mce_out;
-       /* Count indicator to show errors not got */
-       unsigned                mce_overrun;
        /* DCLK Frequency used for computing scrub rate */
        int                     dclk_freq;
  
@@@ -1792,56 -1782,15 +1782,15 @@@ static void i7core_mce_output_error(str
   *    i7core_check_error      Retrieve and process errors reported by the
   *                            hardware. Called by the Core module.
   */
- static void i7core_check_error(struct mem_ctl_info *mci)
+ static void i7core_check_error(struct mem_ctl_info *mci, struct mce *m)
  {
        struct i7core_pvt *pvt = mci->pvt_info;
-       int i;
-       unsigned count = 0;
-       struct mce *m;
  
-       /*
-        * MCE first step: Copy all mce errors into a temporary buffer
-        * We use a double buffering here, to reduce the risk of
-        * losing an error.
-        */
-       smp_rmb();
-       count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
-               % MCE_LOG_LEN;
-       if (!count)
-               goto check_ce_error;
-       m = pvt->mce_outentry;
-       if (pvt->mce_in + count > MCE_LOG_LEN) {
-               unsigned l = MCE_LOG_LEN - pvt->mce_in;
-               memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
-               smp_wmb();
-               pvt->mce_in = 0;
-               count -= l;
-               m += l;
-       }
-       memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
-       smp_wmb();
-       pvt->mce_in += count;
-       smp_rmb();
-       if (pvt->mce_overrun) {
-               i7core_printk(KERN_ERR, "Lost %d memory errors\n",
-                             pvt->mce_overrun);
-               smp_wmb();
-               pvt->mce_overrun = 0;
-       }
-       /*
-        * MCE second step: parse errors and display
-        */
-       for (i = 0; i < count; i++)
-               i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
+       i7core_mce_output_error(mci, m);
  
        /*
         * Now, let's increment CE error counts
         */
- check_ce_error:
        if (!pvt->is_registered)
                i7core_udimm_check_mc_ecc_err(mci);
        else
  }
  
  /*
-  * i7core_mce_check_error     Replicates mcelog routine to get errors
-  *                            This routine simply queues mcelog errors, and
-  *                            return. The error itself should be handled later
-  *                            by i7core_check_error.
-  * WARNING: As this routine should be called at NMI time, extra care should
-  * be taken to avoid deadlocks, and to be as fast as possible.
+  * Check that logging is enabled and that this is the right type
+  * of error for us to handle.
   */
  static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
                                  void *data)
  
        i7_dev = get_i7core_dev(mce->socketid);
        if (!i7_dev)
 -              return NOTIFY_BAD;
 +              return NOTIFY_DONE;
  
        mci = i7_dev->mci;
        pvt = mci->pvt_info;
        if (mce->bank != 8)
                return NOTIFY_DONE;
  
-       smp_rmb();
-       if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
-               smp_wmb();
-               pvt->mce_overrun++;
-               return NOTIFY_DONE;
-       }
-       /* Copy memory error at the ringbuffer */
-       memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
-       smp_wmb();
-       pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
-       /* Handle fatal errors immediately */
-       if (mce->mcgstatus & 1)
-               i7core_check_error(mci);
+       i7core_check_error(mci, mce);
  
        /* Advise mcelog that the errors were handled */
        return NOTIFY_STOP;
@@@ -2243,8 -2174,6 +2174,6 @@@ static int i7core_register_mci(struct i
        get_dimm_config(mci);
        /* record ptr to the generic device */
        mci->pdev = &i7core_dev->pdev[0]->dev;
-       /* Set the function pointer to an actual operation function */
-       mci->edac_check = i7core_check_error;
  
        /* Enable scrubrate setting */
        if (pvt->enable_scrub)
diff --combined drivers/edac/sb_edac.c
index 8bf745d2da7e1750571860d58be00d053579f301,be398e0cf08a41689411b6ce676191fe725964b2..b4d0bf6534cf43732df678eafd956fe39209ce03
@@@ -21,6 -21,8 +21,8 @@@
  #include <linux/smp.h>
  #include <linux/bitmap.h>
  #include <linux/math64.h>
+ #include <linux/mod_devicetable.h>
+ #include <asm/cpu_device_id.h>
  #include <asm/processor.h>
  #include <asm/mce.h>
  
@@@ -28,8 -30,6 +30,6 @@@
  
  /* Static vars */
  static LIST_HEAD(sbridge_edac_list);
- static DEFINE_MUTEX(sbridge_edac_lock);
- static int probed;
  
  /*
   * Alter this version for the module when modifications are made
@@@ -362,18 -362,7 +362,8 @@@ struct sbridge_pvt 
  
        /* Memory type detection */
        bool                    is_mirrored, is_lockstep, is_close_pg;
 +      bool                    is_chan_hash;
  
-       /* Fifo double buffers */
-       struct mce              mce_entry[MCE_LOG_LEN];
-       struct mce              mce_outentry[MCE_LOG_LEN];
-       /* Fifo in/out counters */
-       unsigned                mce_in, mce_out;
-       /* Count indicator to show errors not got */
-       unsigned                mce_overrun;
        /* Memory description */
        u64                     tolm, tohm;
        struct knl_pvt knl;
@@@ -662,18 -651,6 +652,6 @@@ static const struct pci_id_table pci_de
        {0,}                    /* 0 terminated list. */
  };
  
- /*
-  *    pci_device_id   table for which devices we are looking for
-  */
- static const struct pci_device_id sbridge_pci_tbl[] = {
-       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0)},
-       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)},
-       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0)},
-       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0)},
-       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0)},
-       {0,}                    /* 0 terminated list. */
- };
  
  /****************************************************************************
                        Ancillary status routines
@@@ -1061,20 -1038,6 +1039,20 @@@ static inline u8 sad_pkg_ha(u8 pkg
        return (pkg >> 2) & 0x1;
  }
  
 +static int haswell_chan_hash(int idx, u64 addr)
 +{
 +      int i;
 +
 +      /*
 +       * XOR even bits from 12:26 to bit0 of idx,
 +       *     odd bits from 13:27 to bit1
 +       */
 +      for (i = 12; i < 28; i += 2)
 +              idx ^= (addr >> i) & 3;
 +
 +      return idx;
 +}
 +
  /****************************************************************************
                        Memory check routines
   ****************************************************************************/
@@@ -1631,10 -1594,6 +1609,10 @@@ static int get_dimm_config(struct mem_c
                KNL_MAX_CHANNELS : NUM_CHANNELS;
        u64 knl_mc_sizes[KNL_MAX_CHANNELS];
  
 +      if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL) {
 +              pci_read_config_dword(pvt->pci_ha0, HASWELL_HASYSDEFEATURE2, &reg);
 +              pvt->is_chan_hash = GET_BITFIELD(reg, 21, 21);
 +      }
        if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL ||
                        pvt->info.type == KNIGHTS_LANDING)
                pci_read_config_dword(pvt->pci_sad1, SAD_TARGET, &reg);
@@@ -2137,15 -2096,12 +2115,15 @@@ static int get_memory_error_data(struc
        }
  
        ch_way = TAD_CH(reg) + 1;
 -      sck_way = 1 << TAD_SOCK(reg);
 +      sck_way = TAD_SOCK(reg);
  
        if (ch_way == 3)
                idx = addr >> 6;
 -      else
 +      else {
                idx = (addr >> (6 + sck_way + shiftup)) & 0x3;
 +              if (pvt->is_chan_hash)
 +                      idx = haswell_chan_hash(idx, addr);
 +      }
        idx = idx % ch_way;
  
        /*
                switch(ch_way) {
                case 2:
                case 4:
 -                      sck_xch = 1 << sck_way * (ch_way >> 1);
 +                      sck_xch = (1 << sck_way) * (ch_way >> 1);
                        break;
                default:
                        sprintf(msg, "Invalid mirror set. Can't decode addr");
  
        ch_addr = addr - offset;
        ch_addr >>= (6 + shiftup);
 -      ch_addr /= ch_way * sck_way;
 +      ch_addr /= sck_xch;
        ch_addr <<= (6 + shiftup);
        ch_addr |= addr & ((1 << (6 + shiftup)) - 1);
  
@@@ -3097,63 -3053,8 +3075,8 @@@ err_parsing
  }
  
  /*
-  *    sbridge_check_error     Retrieve and process errors reported by the
-  *                            hardware. Called by the Core module.
-  */
- static void sbridge_check_error(struct mem_ctl_info *mci)
- {
-       struct sbridge_pvt *pvt = mci->pvt_info;
-       int i;
-       unsigned count = 0;
-       struct mce *m;
-       /*
-        * MCE first step: Copy all mce errors into a temporary buffer
-        * We use a double buffering here, to reduce the risk of
-        * loosing an error.
-        */
-       smp_rmb();
-       count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in)
-               % MCE_LOG_LEN;
-       if (!count)
-               return;
-       m = pvt->mce_outentry;
-       if (pvt->mce_in + count > MCE_LOG_LEN) {
-               unsigned l = MCE_LOG_LEN - pvt->mce_in;
-               memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
-               smp_wmb();
-               pvt->mce_in = 0;
-               count -= l;
-               m += l;
-       }
-       memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
-       smp_wmb();
-       pvt->mce_in += count;
-       smp_rmb();
-       if (pvt->mce_overrun) {
-               sbridge_printk(KERN_ERR, "Lost %d memory errors\n",
-                             pvt->mce_overrun);
-               smp_wmb();
-               pvt->mce_overrun = 0;
-       }
-       /*
-        * MCE second step: parse errors and display
-        */
-       for (i = 0; i < count; i++)
-               sbridge_mce_output_error(mci, &pvt->mce_outentry[i]);
- }
- /*
-  * sbridge_mce_check_error    Replicates mcelog routine to get errors
-  *                            This routine simply queues mcelog errors, and
-  *                            return. The error itself should be handled later
-  *                            by sbridge_check_error.
-  * WARNING: As this routine should be called at NMI time, extra care should
-  * be taken to avoid deadlocks, and to be as fast as possible.
+  * Check that logging is enabled and that this is the right type
+  * of error for us to handle.
   */
  static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val,
                                   void *data)
  
        mci = get_mci_for_node_id(mce->socketid);
        if (!mci)
 -              return NOTIFY_BAD;
 +              return NOTIFY_DONE;
        pvt = mci->pvt_info;
  
        /*
                          "%u APIC %x\n", mce->cpuvendor, mce->cpuid,
                          mce->time, mce->socketid, mce->apicid);
  
-       smp_rmb();
-       if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) {
-               smp_wmb();
-               pvt->mce_overrun++;
-               return NOTIFY_DONE;
-       }
-       /* Copy memory error at the ringbuffer */
-       memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
-       smp_wmb();
-       pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN;
-       /* Handle fatal errors immediately */
-       if (mce->mcgstatus & 1)
-               sbridge_check_error(mci);
+       sbridge_mce_output_error(mci, mce);
  
        /* Advice mcelog that the error were handled */
        return NOTIFY_STOP;
@@@ -3298,9 -3185,6 +3207,6 @@@ static int sbridge_register_mci(struct 
        mci->dev_name = pci_name(pdev);
        mci->ctl_page_to_phys = NULL;
  
-       /* Set the function pointer to an actual operation function */
-       mci->edac_check = sbridge_check_error;
        pvt->info.type = type;
        switch (type) {
        case IVY_BRIDGE:
@@@ -3448,62 -3332,40 +3354,40 @@@ fail0
        return rc;
  }
  
+ #define ICPU(model, table) \
+       { X86_VENDOR_INTEL, 6, model, 0, (unsigned long)&table }
+ /* Order here must match "enum type" */
+ static const struct x86_cpu_id sbridge_cpuids[] = {
+       ICPU(0x2d, pci_dev_descr_sbridge_table),        /* SANDY_BRIDGE */
+       ICPU(0x3e, pci_dev_descr_ibridge_table),        /* IVY_BRIDGE */
+       ICPU(0x3f, pci_dev_descr_haswell_table),        /* HASWELL */
+       ICPU(0x4f, pci_dev_descr_broadwell_table),      /* BROADWELL */
+       ICPU(0x57, pci_dev_descr_knl_table),            /* KNIGHTS_LANDING */
+       { }
+ };
+ MODULE_DEVICE_TABLE(x86cpu, sbridge_cpuids);
  /*
-  *    sbridge_probe   Probe for ONE instance of device to see if it is
+  *    sbridge_probe   Get all devices and register memory controllers
   *                    present.
   *    return:
   *            0 for FOUND a device
   *            < 0 for error code
   */
  
- static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ static int sbridge_probe(const struct x86_cpu_id *id)
  {
        int rc = -ENODEV;
        u8 mc, num_mc = 0;
        struct sbridge_dev *sbridge_dev;
-       enum type type = SANDY_BRIDGE;
+       struct pci_id_table *ptable = (struct pci_id_table *)id->driver_data;
  
        /* get the pci devices we want to reserve for our use */
-       mutex_lock(&sbridge_edac_lock);
-       /*
-        * All memory controllers are allocated at the first pass.
-        */
-       if (unlikely(probed >= 1)) {
-               mutex_unlock(&sbridge_edac_lock);
-               return -ENODEV;
-       }
-       probed++;
+       rc = sbridge_get_all_devices(&num_mc, ptable);
  
-       switch (pdev->device) {
-       case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA:
-               rc = sbridge_get_all_devices(&num_mc,
-                                       pci_dev_descr_ibridge_table);
-               type = IVY_BRIDGE;
-               break;
-       case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0:
-               rc = sbridge_get_all_devices(&num_mc,
-                                       pci_dev_descr_sbridge_table);
-               type = SANDY_BRIDGE;
-               break;
-       case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0:
-               rc = sbridge_get_all_devices(&num_mc,
-                                       pci_dev_descr_haswell_table);
-               type = HASWELL;
-               break;
-       case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0:
-               rc = sbridge_get_all_devices(&num_mc,
-                                       pci_dev_descr_broadwell_table);
-               type = BROADWELL;
-           break;
-       case PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0:
-               rc = sbridge_get_all_devices_knl(&num_mc,
-                                       pci_dev_descr_knl_table);
-               type = KNIGHTS_LANDING;
-               break;
-       }
        if (unlikely(rc < 0)) {
-               edac_dbg(0, "couldn't get all devices for 0x%x\n", pdev->device);
+               edac_dbg(0, "couldn't get all devices\n");
                goto fail0;
        }
  
                         mc, mc + 1, num_mc);
  
                sbridge_dev->mc = mc++;
-               rc = sbridge_register_mci(sbridge_dev, type);
+               rc = sbridge_register_mci(sbridge_dev, id - sbridge_cpuids);
                if (unlikely(rc < 0))
                        goto fail1;
        }
  
        sbridge_printk(KERN_INFO, "%s\n", SBRIDGE_REVISION);
  
-       mutex_unlock(&sbridge_edac_lock);
        return 0;
  
  fail1:
  
        sbridge_put_all_devices();
  fail0:
-       mutex_unlock(&sbridge_edac_lock);
        return rc;
  }
  
  /*
-  *    sbridge_remove  destructor for one instance of device
+  *    sbridge_remove  cleanup
   *
   */
- static void sbridge_remove(struct pci_dev *pdev)
+ static void sbridge_remove(void)
  {
        struct sbridge_dev *sbridge_dev;
  
        edac_dbg(0, "\n");
  
-       /*
-        * we have a trouble here: pdev value for removal will be wrong, since
-        * it will point to the X58 register used to detect that the machine
-        * is a Nehalem or upper design. However, due to the way several PCI
-        * devices are grouped together to provide MC functionality, we need
-        * to use a different method for releasing the devices
-        */
-       mutex_lock(&sbridge_edac_lock);
-       if (unlikely(!probed)) {
-               mutex_unlock(&sbridge_edac_lock);
-               return;
-       }
        list_for_each_entry(sbridge_dev, &sbridge_edac_list, list)
                sbridge_unregister_mci(sbridge_dev);
  
        /* Release PCI resources */
        sbridge_put_all_devices();
-       probed--;
-       mutex_unlock(&sbridge_edac_lock);
  }
  
- MODULE_DEVICE_TABLE(pci, sbridge_pci_tbl);
- /*
-  *    sbridge_driver  pci_driver structure for this module
-  *
-  */
- static struct pci_driver sbridge_driver = {
-       .name     = "sbridge_edac",
-       .probe    = sbridge_probe,
-       .remove   = sbridge_remove,
-       .id_table = sbridge_pci_tbl,
- };
  /*
   *    sbridge_init            Module entry function
   *                    Try to initialize this module for its devices
   */
  static int __init sbridge_init(void)
  {
-       int pci_rc;
+       const struct x86_cpu_id *id;
+       int rc;
  
        edac_dbg(2, "\n");
  
+       id = x86_match_cpu(sbridge_cpuids);
+       if (!id)
+               return -ENODEV;
        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
        opstate_init();
  
-       pci_rc = pci_register_driver(&sbridge_driver);
-       if (pci_rc >= 0) {
+       rc = sbridge_probe(id);
+       if (rc >= 0) {
                mce_register_decode_chain(&sbridge_mce_dec);
                if (get_edac_report_status() == EDAC_REPORTING_DISABLED)
                        sbridge_printk(KERN_WARNING, "Loading driver, error reporting disabled.\n");
        }
  
        sbridge_printk(KERN_ERR, "Failed to register device with error %d.\n",
-                     pci_rc);
+                     rc);
  
-       return pci_rc;
+       return rc;
  }
  
  /*
  static void __exit sbridge_exit(void)
  {
        edac_dbg(2, "\n");
-       pci_unregister_driver(&sbridge_driver);
+       sbridge_remove();
        mce_unregister_decode_chain(&sbridge_mce_dec);
  }