1 // SPDX-License-Identifier: GPL-2.0-or-later 1 2 /* 3 * 4 * Copyright (c) 2005 Linas Vepstas <linas@lin 5 */ 6 7 #include <linux/delay.h> 8 #include <linux/list.h> 9 #include <linux/sched.h> 10 #include <linux/semaphore.h> 11 #include <linux/pci.h> 12 #include <linux/slab.h> 13 #include <linux/kthread.h> 14 #include <asm/eeh_event.h> 15 #include <asm/ppc-pci.h> 16 17 /** Overview: 18 * EEH error states may be detected within ex 19 * however, the recovery processing needs to 20 * in a normal kernel context and not an inte 21 * This pair of routines creates an event and 22 * work-queue, where a worker thread can driv 23 */ 24 25 static DEFINE_SPINLOCK(eeh_eventlist_lock); 26 static DECLARE_COMPLETION(eeh_eventlist_event) 27 static LIST_HEAD(eeh_eventlist); 28 29 /** 30 * eeh_event_handler - Dispatch EEH events. 31 * @dummy - unused 32 * 33 * The detection of a frozen slot can occur in 34 * where it can be hard to do anything about i 35 * routine is to pull these detection events o 36 * of the interrupt handler, and re-dispatch t 37 * at a later time in a normal context. 38 */ 39 static int eeh_event_handler(void * dummy) 40 { 41 unsigned long flags; 42 struct eeh_event *event; 43 44 while (!kthread_should_stop()) { 45 if (wait_for_completion_interr 46 break; 47 48 /* Fetch EEH event from the qu 49 spin_lock_irqsave(&eeh_eventli 50 event = NULL; 51 if (!list_empty(&eeh_eventlist 52 event = list_entry(eeh 53 str 54 list_del(&event->list) 55 } 56 spin_unlock_irqrestore(&eeh_ev 57 if (!event) 58 continue; 59 60 /* We might have event without 61 if (event->pe) 62 eeh_handle_normal_even 63 else 64 eeh_handle_special_eve 65 66 kfree(event); 67 } 68 69 return 0; 70 } 71 72 /** 73 * eeh_event_init - Start kernel thread to han 74 * 75 * This routine is called to start the kernel 76 * EEH event. 77 */ 78 int eeh_event_init(void) 79 { 80 struct task_struct *t; 81 int ret = 0; 82 83 t = kthread_run(eeh_event_handler, NUL 84 if (IS_ERR(t)) { 85 ret = PTR_ERR(t); 86 pr_err("%s: Failed to start EE 87 __func__, ret); 88 return ret; 89 } 90 91 return 0; 92 } 93 94 /** 95 * eeh_send_failure_event - Generate a PCI err 96 * @pe: EEH PE 97 * 98 * This routine can be called within an interr 99 * the actual event will be delivered in a nor 100 * (from a workqueue). 101 */ 102 int __eeh_send_failure_event(struct eeh_pe *pe 103 { 104 unsigned long flags; 105 struct eeh_event *event; 106 107 event = kzalloc(sizeof(*event), GFP_AT 108 if (!event) { 109 pr_err("EEH: out of memory, ev 110 return -ENOMEM; 111 } 112 event->pe = pe; 113 114 /* 115 * Mark the PE as recovering before in 116 * This prevents the PE from being fre 117 * while the PE is sitting in the even 118 */ 119 if (pe) { 120 #ifdef CONFIG_STACKTRACE 121 /* 122 * Save the current stack trac 123 * event handler thread. 124 */ 125 pe->trace_entries = stack_trac 126 ARRAY 127 #endif /* CONFIG_STACKTRACE */ 128 129 eeh_pe_state_mark(pe, EEH_PE_R 130 } 131 132 /* We may or may not be called in an i 133 spin_lock_irqsave(&eeh_eventlist_lock, 134 list_add(&event->list, &eeh_eventlist) 135 spin_unlock_irqrestore(&eeh_eventlist_ 136 137 /* For EEH deamon to knick in */ 138 complete(&eeh_eventlist_event); 139 140 return 0; 141 } 142 143 int eeh_send_failure_event(struct eeh_pe *pe) 144 { 145 /* 146 * If we've manually suppressed recove 147 * then just drop it on the floor. 148 */ 149 if (eeh_debugfs_no_recover) { 150 pr_err("EEH: Event dropped due 151 return 0; 152 } 153 154 return __eeh_send_failure_event(pe); 155 } 156 157 /** 158 * eeh_remove_event - Remove EEH event from th 159 * @pe: Event binding to the PE 160 * @force: Event will be removed unconditional 161 * 162 * On PowerNV platform, we might have subseque 163 * is part of the former one. For that case, t 164 * coming events are totally duplicated and un 165 * they should be removed. 166 */ 167 void eeh_remove_event(struct eeh_pe *pe, bool 168 { 169 unsigned long flags; 170 struct eeh_event *event, *tmp; 171 172 /* 173 * If we have NULL PE passed in, we ha 174 * or we're sure we can report all exi 175 * by the caller. 176 * 177 * With "force", the event with associ 178 * have been isolated, the event won't 179 * to avoid event lost. 180 */ 181 spin_lock_irqsave(&eeh_eventlist_lock, 182 list_for_each_entry_safe(event, tmp, & 183 if (!force && event->pe && 184 (event->pe->state & EEH_PE 185 continue; 186 187 if (!pe) { 188 list_del(&event->list) 189 kfree(event); 190 } else if (pe->type & EEH_PE_P 191 if (event->pe && event 192 list_del(&even 193 kfree(event); 194 } 195 } else if (event->pe == pe) { 196 list_del(&event->list) 197 kfree(event); 198 } 199 } 200 spin_unlock_irqrestore(&eeh_eventlist_ 201 } 202
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.