cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mthca_catas.c (5280B)


      1/*
      2 * Copyright (c) 2005 Cisco Systems.  All rights reserved.
      3 *
      4 * This software is available to you under a choice of one of two
      5 * licenses.  You may choose to be licensed under the terms of the GNU
      6 * General Public License (GPL) Version 2, available from the file
      7 * COPYING in the main directory of this source tree, or the
      8 * OpenIB.org BSD license below:
      9 *
     10 *     Redistribution and use in source and binary forms, with or
     11 *     without modification, are permitted provided that the following
     12 *     conditions are met:
     13 *
     14 *      - Redistributions of source code must retain the above
     15 *        copyright notice, this list of conditions and the following
     16 *        disclaimer.
     17 *
     18 *      - Redistributions in binary form must reproduce the above
     19 *        copyright notice, this list of conditions and the following
     20 *        disclaimer in the documentation and/or other materials
     21 *        provided with the distribution.
     22 *
     23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     30 * SOFTWARE.
     31 */
     32
     33#include <linux/jiffies.h>
     34#include <linux/module.h>
     35#include <linux/timer.h>
     36#include <linux/workqueue.h>
     37
     38#include "mthca_dev.h"
     39
     40enum {
     41	MTHCA_CATAS_POLL_INTERVAL	= 5 * HZ,
     42
     43	MTHCA_CATAS_TYPE_INTERNAL	= 0,
     44	MTHCA_CATAS_TYPE_UPLINK		= 3,
     45	MTHCA_CATAS_TYPE_DDR		= 4,
     46	MTHCA_CATAS_TYPE_PARITY		= 5,
     47};
     48
     49static DEFINE_SPINLOCK(catas_lock);
     50
     51static LIST_HEAD(catas_list);
     52static struct workqueue_struct *catas_wq;
     53static struct work_struct catas_work;
     54
     55static int catas_reset_disable;
     56module_param_named(catas_reset_disable, catas_reset_disable, int, 0644);
     57MODULE_PARM_DESC(catas_reset_disable, "disable reset on catastrophic event if nonzero");
     58
     59static void catas_reset(struct work_struct *work)
     60{
     61	struct mthca_dev *dev, *tmpdev;
     62	LIST_HEAD(tlist);
     63	int ret;
     64
     65	mutex_lock(&mthca_device_mutex);
     66
     67	spin_lock_irq(&catas_lock);
     68	list_splice_init(&catas_list, &tlist);
     69	spin_unlock_irq(&catas_lock);
     70
     71	list_for_each_entry_safe(dev, tmpdev, &tlist, catas_err.list) {
     72		struct pci_dev *pdev = dev->pdev;
     73		ret = __mthca_restart_one(dev->pdev);
     74		/* 'dev' now is not valid */
     75		if (ret)
     76			printk(KERN_ERR "mthca %s: Reset failed (%d)\n",
     77			       pci_name(pdev), ret);
     78		else {
     79			struct mthca_dev *d = pci_get_drvdata(pdev);
     80			mthca_dbg(d, "Reset succeeded\n");
     81		}
     82	}
     83
     84	mutex_unlock(&mthca_device_mutex);
     85}
     86
     87static void handle_catas(struct mthca_dev *dev)
     88{
     89	struct ib_event event;
     90	unsigned long flags;
     91	const char *type;
     92	int i;
     93
     94	event.device = &dev->ib_dev;
     95	event.event  = IB_EVENT_DEVICE_FATAL;
     96	event.element.port_num = 0;
     97	dev->active = false;
     98
     99	ib_dispatch_event(&event);
    100
    101	switch (swab32(readl(dev->catas_err.map)) >> 24) {
    102	case MTHCA_CATAS_TYPE_INTERNAL:
    103		type = "internal error";
    104		break;
    105	case MTHCA_CATAS_TYPE_UPLINK:
    106		type = "uplink bus error";
    107		break;
    108	case MTHCA_CATAS_TYPE_DDR:
    109		type = "DDR data error";
    110		break;
    111	case MTHCA_CATAS_TYPE_PARITY:
    112		type = "internal parity error";
    113		break;
    114	default:
    115		type = "unknown error";
    116		break;
    117	}
    118
    119	mthca_err(dev, "Catastrophic error detected: %s\n", type);
    120	for (i = 0; i < dev->catas_err.size; ++i)
    121		mthca_err(dev, "  buf[%02x]: %08x\n",
    122			  i, swab32(readl(dev->catas_err.map + i)));
    123
    124	if (catas_reset_disable)
    125		return;
    126
    127	spin_lock_irqsave(&catas_lock, flags);
    128	list_add(&dev->catas_err.list, &catas_list);
    129	queue_work(catas_wq, &catas_work);
    130	spin_unlock_irqrestore(&catas_lock, flags);
    131}
    132
    133static void poll_catas(struct timer_list *t)
    134{
    135	struct mthca_dev *dev = from_timer(dev, t, catas_err.timer);
    136	int i;
    137
    138	for (i = 0; i < dev->catas_err.size; ++i)
    139		if (readl(dev->catas_err.map + i)) {
    140			handle_catas(dev);
    141			return;
    142		}
    143
    144	mod_timer(&dev->catas_err.timer,
    145		  round_jiffies(jiffies + MTHCA_CATAS_POLL_INTERVAL));
    146}
    147
    148void mthca_start_catas_poll(struct mthca_dev *dev)
    149{
    150	phys_addr_t addr;
    151
    152	timer_setup(&dev->catas_err.timer, poll_catas, 0);
    153	dev->catas_err.map  = NULL;
    154
    155	addr = pci_resource_start(dev->pdev, 0) +
    156		((pci_resource_len(dev->pdev, 0) - 1) &
    157		 dev->catas_err.addr);
    158
    159	dev->catas_err.map = ioremap(addr, dev->catas_err.size * 4);
    160	if (!dev->catas_err.map) {
    161		mthca_warn(dev, "couldn't map catastrophic error region "
    162			   "at 0x%llx/0x%x\n", (unsigned long long) addr,
    163			   dev->catas_err.size * 4);
    164		return;
    165	}
    166
    167	dev->catas_err.timer.expires  = jiffies + MTHCA_CATAS_POLL_INTERVAL;
    168	INIT_LIST_HEAD(&dev->catas_err.list);
    169	add_timer(&dev->catas_err.timer);
    170}
    171
    172void mthca_stop_catas_poll(struct mthca_dev *dev)
    173{
    174	del_timer_sync(&dev->catas_err.timer);
    175
    176	if (dev->catas_err.map)
    177		iounmap(dev->catas_err.map);
    178
    179	spin_lock_irq(&catas_lock);
    180	list_del(&dev->catas_err.list);
    181	spin_unlock_irq(&catas_lock);
    182}
    183
    184int __init mthca_catas_init(void)
    185{
    186	INIT_WORK(&catas_work, catas_reset);
    187
    188	catas_wq = alloc_ordered_workqueue("mthca_catas", WQ_MEM_RECLAIM);
    189	if (!catas_wq)
    190		return -ENOMEM;
    191
    192	return 0;
    193}
    194
    195void mthca_catas_cleanup(void)
    196{
    197	destroy_workqueue(catas_wq);
    198}