Path: blob/master/arch/powerpc/platforms/pseries/eeh_event.c
10818 views
/*1* eeh_event.c2*3* This program is free software; you can redistribute it and/or modify4* it under the terms of the GNU General Public License as published by5* the Free Software Foundation; either version 2 of the License, or6* (at your option) any later version.7*8* This program is distributed in the hope that it will be useful,9* but WITHOUT ANY WARRANTY; without even the implied warranty of10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11* GNU General Public License for more details.12*13* You should have received a copy of the GNU General Public License14* along with this program; if not, write to the Free Software15* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA16*17* Copyright (c) 2005 Linas Vepstas <[email protected]>18*/1920#include <linux/delay.h>21#include <linux/list.h>22#include <linux/mutex.h>23#include <linux/pci.h>24#include <linux/slab.h>25#include <linux/workqueue.h>26#include <asm/eeh_event.h>27#include <asm/ppc-pci.h>2829/** Overview:30* EEH error states may be detected within exception handlers;31* however, the recovery processing needs to occur asynchronously32* in a normal kernel context and not an interrupt context.33* This pair of routines creates an event and queues it onto a34* work-queue, where a worker thread can drive recovery.35*/3637/* EEH event workqueue setup. */38static DEFINE_SPINLOCK(eeh_eventlist_lock);39LIST_HEAD(eeh_eventlist);40static void eeh_thread_launcher(struct work_struct *);41DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);4243/* Serialize reset sequences for a given pci device */44DEFINE_MUTEX(eeh_event_mutex);4546/**47* eeh_event_handler - dispatch EEH events.48* @dummy - unused49*50* The detection of a frozen slot can occur inside an interrupt,51* where it can be hard to do anything about it. The goal of this52* routine is to pull these detection events out of the context53* of the interrupt handler, and re-dispatch them for processing54* at a later time in a normal context.55*/56static int eeh_event_handler(void * dummy)57{58unsigned long flags;59struct eeh_event *event;60struct pci_dn *pdn;6162daemonize ("eehd");63set_current_state(TASK_INTERRUPTIBLE);6465spin_lock_irqsave(&eeh_eventlist_lock, flags);66event = NULL;6768/* Unqueue the event, get ready to process. */69if (!list_empty(&eeh_eventlist)) {70event = list_entry(eeh_eventlist.next, struct eeh_event, list);71list_del(&event->list);72}73spin_unlock_irqrestore(&eeh_eventlist_lock, flags);7475if (event == NULL)76return 0;7778/* Serialize processing of EEH events */79mutex_lock(&eeh_event_mutex);80eeh_mark_slot(event->dn, EEH_MODE_RECOVERING);8182printk(KERN_INFO "EEH: Detected PCI bus error on device %s\n",83eeh_pci_name(event->dev));8485pdn = handle_eeh_events(event);8687eeh_clear_slot(event->dn, EEH_MODE_RECOVERING);88pci_dev_put(event->dev);89kfree(event);90mutex_unlock(&eeh_event_mutex);9192/* If there are no new errors after an hour, clear the counter. */93if (pdn && pdn->eeh_freeze_count>0) {94msleep_interruptible (3600*1000);95if (pdn->eeh_freeze_count>0)96pdn->eeh_freeze_count--;97}9899return 0;100}101102/**103* eeh_thread_launcher104* @dummy - unused105*/106static void eeh_thread_launcher(struct work_struct *dummy)107{108if (kernel_thread(eeh_event_handler, NULL, CLONE_KERNEL) < 0)109printk(KERN_ERR "Failed to start EEH daemon\n");110}111112/**113* eeh_send_failure_event - generate a PCI error event114* @dev pci device115*116* This routine can be called within an interrupt context;117* the actual event will be delivered in a normal context118* (from a workqueue).119*/120int eeh_send_failure_event (struct device_node *dn,121struct pci_dev *dev)122{123unsigned long flags;124struct eeh_event *event;125const char *location;126127if (!mem_init_done) {128printk(KERN_ERR "EEH: event during early boot not handled\n");129location = of_get_property(dn, "ibm,loc-code", NULL);130printk(KERN_ERR "EEH: device node = %s\n", dn->full_name);131printk(KERN_ERR "EEH: PCI location = %s\n", location);132return 1;133}134event = kmalloc(sizeof(*event), GFP_ATOMIC);135if (event == NULL) {136printk (KERN_ERR "EEH: out of memory, event not handled\n");137return 1;138}139140if (dev)141pci_dev_get(dev);142143event->dn = dn;144event->dev = dev;145146/* We may or may not be called in an interrupt context */147spin_lock_irqsave(&eeh_eventlist_lock, flags);148list_add(&event->list, &eeh_eventlist);149spin_unlock_irqrestore(&eeh_eventlist_lock, flags);150151schedule_work(&eeh_event_wq);152153return 0;154}155156/********************** END OF FILE ******************************/157158159