diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-10-04 09:15:40 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-05-10 10:49:30 -0400 |
commit | ca9c90ba09ca3c9799319f46a56f397afbf617c2 (patch) | |
tree | 63085920246ac68f32b80606eac2201b0e8d005b /drivers/edac | |
parent | b968759ee7102f86fec5f3349f7a8ab4556884a3 (diff) |
i7core_edac: Use a lockless ringbuffer
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/i7core_edac.c | 83 |
1 files changed, 55 insertions, 28 deletions
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 97f6d1759c99..94aeca011ac4 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c | |||
@@ -28,7 +28,6 @@ | |||
28 | #include <linux/edac.h> | 28 | #include <linux/edac.h> |
29 | #include <linux/mmzone.h> | 29 | #include <linux/mmzone.h> |
30 | #include <linux/edac_mce.h> | 30 | #include <linux/edac_mce.h> |
31 | #include <linux/spinlock.h> | ||
32 | #include <linux/smp.h> | 31 | #include <linux/smp.h> |
33 | #include <asm/processor.h> | 32 | #include <asm/processor.h> |
34 | 33 | ||
@@ -239,9 +238,16 @@ struct i7core_pvt { | |||
239 | 238 | ||
240 | /* mcelog glue */ | 239 | /* mcelog glue */ |
241 | struct edac_mce edac_mce; | 240 | struct edac_mce edac_mce; |
241 | |||
242 | /* Fifo double buffers */ | ||
242 | struct mce mce_entry[MCE_LOG_LEN]; | 243 | struct mce mce_entry[MCE_LOG_LEN]; |
243 | unsigned mce_count; | 244 | struct mce mce_outentry[MCE_LOG_LEN]; |
244 | spinlock_t mce_lock; | 245 | |
246 | /* Fifo in/out counters */ | ||
247 | unsigned mce_in, mce_out; | ||
248 | |||
249 | /* Count indicator to show errors not got */ | ||
250 | unsigned mce_overrun; | ||
245 | }; | 251 | }; |
246 | 252 | ||
247 | /* Static vars */ | 253 | /* Static vars */ |
@@ -1617,30 +1623,50 @@ static void i7core_check_error(struct mem_ctl_info *mci) | |||
1617 | struct i7core_pvt *pvt = mci->pvt_info; | 1623 | struct i7core_pvt *pvt = mci->pvt_info; |
1618 | int i; | 1624 | int i; |
1619 | unsigned count = 0; | 1625 | unsigned count = 0; |
1620 | struct mce *m = NULL; | 1626 | struct mce *m; |
1621 | unsigned long flags; | ||
1622 | 1627 | ||
1623 | /* Copy all mce errors into a temporary buffer */ | 1628 | /* |
1624 | spin_lock_irqsave(&pvt->mce_lock, flags); | 1629 | * MCE first step: Copy all mce errors into a temporary buffer |
1625 | if (pvt->mce_count) { | 1630 | * We use a double buffering here, to reduce the risk of |
1626 | m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC); | 1631 | * loosing an error. |
1632 | */ | ||
1633 | smp_rmb(); | ||
1634 | count = (pvt->mce_out + sizeof(mce_entry) - pvt->mce_in) | ||
1635 | % sizeof(mce_entry); | ||
1636 | if (!count) | ||
1637 | return; | ||
1627 | 1638 | ||
1628 | if (m) { | 1639 | m = pvt->mce_outentry; |
1629 | count = pvt->mce_count; | 1640 | if (pvt->mce_in + count > sizeof(mce_entry)) { |
1630 | memcpy(m, &pvt->mce_entry, sizeof(*m) * count); | 1641 | unsigned l = sizeof(mce_entry) - pvt->mce_in; |
1631 | } | ||
1632 | pvt->mce_count = 0; | ||
1633 | } | ||
1634 | 1642 | ||
1635 | spin_unlock_irqrestore(&pvt->mce_lock, flags); | 1643 | memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l); |
1644 | smp_wmb(); | ||
1645 | pvt->mce_in = 0; | ||
1646 | count -= l; | ||
1647 | m += l; | ||
1648 | } | ||
1649 | memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count); | ||
1650 | smp_wmb(); | ||
1651 | pvt->mce_in += count; | ||
1652 | |||
1653 | smp_rmb(); | ||
1654 | if (pvt->mce_overrun) { | ||
1655 | i7core_printk(KERN_ERR, "Lost %d memory errors\n", | ||
1656 | pvt->mce_overrun); | ||
1657 | smp_wmb(); | ||
1658 | pvt->mce_overrun = 0; | ||
1659 | } | ||
1636 | 1660 | ||
1637 | /* proccess mcelog errors */ | 1661 | /* |
1662 | * MCE second step: parse errors and display | ||
1663 | */ | ||
1638 | for (i = 0; i < count; i++) | 1664 | for (i = 0; i < count; i++) |
1639 | i7core_mce_output_error(mci, &m[i]); | 1665 | i7core_mce_output_error(mci, &pvt->mce_outentry[i]); |
1640 | 1666 | ||
1641 | kfree(m); | 1667 | /* |
1642 | 1668 | * Now, let's increment CE error counts | |
1643 | /* check memory count errors */ | 1669 | */ |
1644 | if (!pvt->is_registered) | 1670 | if (!pvt->is_registered) |
1645 | i7core_udimm_check_mc_ecc_err(mci); | 1671 | i7core_udimm_check_mc_ecc_err(mci); |
1646 | else | 1672 | else |
@@ -1657,7 +1683,6 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) | |||
1657 | { | 1683 | { |
1658 | struct mem_ctl_info *mci = priv; | 1684 | struct mem_ctl_info *mci = priv; |
1659 | struct i7core_pvt *pvt = mci->pvt_info; | 1685 | struct i7core_pvt *pvt = mci->pvt_info; |
1660 | unsigned long flags; | ||
1661 | 1686 | ||
1662 | /* | 1687 | /* |
1663 | * Just let mcelog handle it if the error is | 1688 | * Just let mcelog handle it if the error is |
@@ -1679,12 +1704,15 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) | |||
1679 | return 0; | 1704 | return 0; |
1680 | } | 1705 | } |
1681 | 1706 | ||
1682 | spin_lock_irqsave(&pvt->mce_lock, flags); | 1707 | smp_rmb(); |
1683 | if (pvt->mce_count < MCE_LOG_LEN) { | 1708 | if ((pvt->mce_out + 1) % sizeof(mce_entry) == pvt->mce_in) { |
1684 | memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce)); | 1709 | smp_wmb(); |
1685 | pvt->mce_count++; | 1710 | pvt->mce_overrun++; |
1711 | return 0; | ||
1686 | } | 1712 | } |
1687 | spin_unlock_irqrestore(&pvt->mce_lock, flags); | 1713 | smp_wmb(); |
1714 | pvt->mce_out = (pvt->mce_out + 1) % sizeof(mce_entry); | ||
1715 | memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce)); | ||
1688 | 1716 | ||
1689 | /* Handle fatal errors immediately */ | 1717 | /* Handle fatal errors immediately */ |
1690 | if (mce->mcgstatus & 1) | 1718 | if (mce->mcgstatus & 1) |
@@ -1777,7 +1805,6 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, | |||
1777 | /* Registers on edac_mce in order to receive memory errors */ | 1805 | /* Registers on edac_mce in order to receive memory errors */ |
1778 | pvt->edac_mce.priv = mci; | 1806 | pvt->edac_mce.priv = mci; |
1779 | pvt->edac_mce.check_error = i7core_mce_check_error; | 1807 | pvt->edac_mce.check_error = i7core_mce_check_error; |
1780 | spin_lock_init(&pvt->mce_lock); | ||
1781 | 1808 | ||
1782 | rc = edac_mce_register(&pvt->edac_mce); | 1809 | rc = edac_mce_register(&pvt->edac_mce); |
1783 | if (unlikely(rc < 0)) { | 1810 | if (unlikely(rc < 0)) { |