eeh_driver.c 17.7 KB
Newer Older
1 2
/*
 * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
3 4
 * Copyright IBM Corp. 2004 2005
 * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
23
 * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
24 25 26
 */
#include <linux/delay.h>
#include <linux/interrupt.h>
27
#include <linux/irq.h>
28
#include <linux/module.h>
29 30 31 32 33 34 35 36
#include <linux/pci.h>
#include <asm/eeh.h>
#include <asm/eeh_event.h>
#include <asm/ppc-pci.h>
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#include <asm/rtas.h>

37 38 39 40 41 42 43
/**
 * eeh_pcid_name - Retrieve name of PCI device driver
 * @pdev: PCI device
 *
 * This routine is used to retrieve the name of PCI device driver
 * if that's valid.
 */
44
static inline const char *eeh_pcid_name(struct pci_dev *pdev)
45
{
46
	if (pdev && pdev->dev.driver)
47 48 49 50
		return pdev->dev.driver->name;
	return "";
}

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
/**
 * eeh_pcid_get - Get the PCI device driver
 * @pdev: PCI device
 *
 * The function is used to retrieve the PCI device driver for
 * the indicated PCI device. Besides, we will increase the reference
 * of the PCI device driver to prevent that being unloaded on
 * the fly. Otherwise, kernel crash would be seen.
 */
static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
{
	if (!pdev || !pdev->driver)
		return NULL;

	if (!try_module_get(pdev->driver->driver.owner))
		return NULL;

	return pdev->driver;
}

/**
 * eeh_pcid_put - Dereference on the PCI device driver
 * @pdev: PCI device
 *
 * The function is called to do dereference on the PCI device
 * driver of the indicated PCI device.
 */
static inline void eeh_pcid_put(struct pci_dev *pdev)
{
	if (!pdev || !pdev->driver)
		return;

	module_put(pdev->driver->driver.owner);
}

86
#if 0
87
static void print_device_node_tree(struct pci_dn *pdn, int dent)
88 89
{
	int i;
90 91 92 93 94
	struct device_node *pc;

	if (!pdn)
		return;
	for (i = 0; i < dent; i++)
95 96 97 98 99
		printk(" ");
	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
		pdn->eeh_pe_config_addr, pdn->node->full_name);
	dent += 3;
100
	pc = pdn->node->child;
101 102 103 104 105 106 107
	while (pc) {
		print_device_node_tree(PCI_DN(pc), dent);
		pc = pc->sibling;
	}
}
#endif

108
/**
109 110 111 112 113 114 115 116
 * eeh_disable_irq - Disable interrupt for the recovering device
 * @dev: PCI device
 *
 * This routine must be called when reporting temporary or permanent
 * error to the particular PCI device to disable interrupt of that
 * device. If the device has enabled MSI or MSI-X interrupt, we needn't
 * do real work because EEH should freeze DMA transfers for those PCI
 * devices encountering EEH errors, which includes MSI or MSI-X.
117 118 119
 */
static void eeh_disable_irq(struct pci_dev *dev)
{
120
	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
121 122 123 124

	/* Don't disable MSI and MSI-X interrupts. They are
	 * effectively disabled by the DMA Stopped state
	 * when an EEH error occurs.
125
	 */
126 127 128
	if (dev->msi_enabled || dev->msix_enabled)
		return;

129
	if (!irq_has_action(dev->irq))
130 131
		return;

G
Gavin Shan 已提交
132
	edev->mode |= EEH_DEV_IRQ_DISABLED;
133 134 135 136
	disable_irq_nosync(dev->irq);
}

/**
137 138 139 140 141
 * eeh_enable_irq - Enable interrupt for the recovering device
 * @dev: PCI device
 *
 * This routine must be called to enable interrupt while failed
 * device could be resumed.
142 143 144
 */
static void eeh_enable_irq(struct pci_dev *dev)
{
145
	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
146

G
Gavin Shan 已提交
147 148
	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
149 150 151 152
		enable_irq(dev->irq);
	}
}

153
/**
154
 * eeh_report_error - Report pci error to each device driver
155
 * @data: eeh device
156
 * @userdata: return value
G
Gavin Shan 已提交
157 158 159
 *
 * Report an EEH error to each device driver, collect up and
 * merge the device driver responses. Cumulative response
160
 * passed back in "userdata".
161
 */
162
static void *eeh_report_error(void *data, void *userdata)
163
{
164 165
	struct eeh_dev *edev = (struct eeh_dev *)data;
	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
166
	enum pci_ers_result rc, *res = userdata;
167
	struct pci_driver *driver;
168

169 170 171 172
	/* We might not have the associated PCI device,
	 * then we should continue for next one.
	 */
	if (!dev) return NULL;
173 174
	dev->error_state = pci_channel_io_frozen;

175 176
	driver = eeh_pcid_get(dev);
	if (!driver) return NULL;
177

178 179
	eeh_disable_irq(dev);

180
	if (!driver->err_handler ||
181 182
	    !driver->err_handler->error_detected) {
		eeh_pcid_put(dev);
183
		return NULL;
184
	}
185

186
	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
187 188 189

	/* A driver that needs a reset trumps all others */
	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
190
	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
191

192
	eeh_pcid_put(dev);
193
	return NULL;
194 195 196
}

/**
197
 * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
198
 * @data: eeh device
199
 * @userdata: return value
200
 *
L
Linas Vepstas 已提交
201 202 203
 * Tells each device driver that IO ports, MMIO and config space I/O
 * are now enabled. Collects up and merges the device driver responses.
 * Cumulative response passed back in "userdata".
204
 */
205
static void *eeh_report_mmio_enabled(void *data, void *userdata)
206
{
207 208
	struct eeh_dev *edev = (struct eeh_dev *)data;
	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
209
	enum pci_ers_result rc, *res = userdata;
210
	struct pci_driver *driver;
211

212 213
	driver = eeh_pcid_get(dev);
	if (!driver) return NULL;
214

215 216 217
	if (!driver->err_handler ||
	    !driver->err_handler->mmio_enabled) {
		eeh_pcid_put(dev);
218
		return NULL;
219
	}
220

221
	rc = driver->err_handler->mmio_enabled(dev);
222 223 224

	/* A driver that needs a reset trumps all others */
	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
225
	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
226

227
	eeh_pcid_put(dev);
228
	return NULL;
229 230
}

231
/**
232
 * eeh_report_reset - Tell device that slot has been reset
233
 * @data: eeh device
234 235 236 237 238 239
 * @userdata: return value
 *
 * This routine must be called while EEH tries to reset particular
 * PCI device so that the associated PCI device driver could take
 * some actions, usually to save data the driver needs so that the
 * driver can work again while the device is recovered.
240
 */
241
static void *eeh_report_reset(void *data, void *userdata)
242
{
243 244
	struct eeh_dev *edev = (struct eeh_dev *)data;
	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
245
	enum pci_ers_result rc, *res = userdata;
246
	struct pci_driver *driver;
247

248
	if (!dev) return NULL;
249 250
	dev->error_state = pci_channel_io_normal;

251 252 253
	driver = eeh_pcid_get(dev);
	if (!driver) return NULL;

254 255
	eeh_enable_irq(dev);

256
	if (!driver->err_handler ||
257 258
	    !driver->err_handler->slot_reset) {
		eeh_pcid_put(dev);
259
		return NULL;
260
	}
261

262
	rc = driver->err_handler->slot_reset(dev);
263 264
	if ((*res == PCI_ERS_RESULT_NONE) ||
	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
265 266
	if (*res == PCI_ERS_RESULT_DISCONNECT &&
	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
267

268
	eeh_pcid_put(dev);
269
	return NULL;
270 271
}

272
/**
273
 * eeh_report_resume - Tell device to resume normal operations
274
 * @data: eeh device
275 276 277 278 279
 * @userdata: return value
 *
 * This routine must be called to notify the device driver that it
 * could resume so that the device driver can do some initialization
 * to make the recovered device work again.
280
 */
281
static void *eeh_report_resume(void *data, void *userdata)
282
{
283 284 285 286 287
	struct eeh_dev *edev = (struct eeh_dev *)data;
	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
	struct pci_driver *driver;

	if (!dev) return NULL;
288 289
	dev->error_state = pci_channel_io_normal;

290 291
	driver = eeh_pcid_get(dev);
	if (!driver) return NULL;
L
Linas Vepstas 已提交
292

293 294
	eeh_enable_irq(dev);

L
Linas Vepstas 已提交
295
	if (!driver->err_handler ||
296 297
	    !driver->err_handler->resume) {
		eeh_pcid_put(dev);
298
		return NULL;
299
	}
300 301

	driver->err_handler->resume(dev);
302

303
	eeh_pcid_put(dev);
304
	return NULL;
305 306
}

307
/**
308
 * eeh_report_failure - Tell device driver that device is dead.
309
 * @data: eeh device
310
 * @userdata: return value
311 312 313 314
 *
 * This informs the device driver that the device is permanently
 * dead, and that no further recovery attempts will be made on it.
 */
315
static void *eeh_report_failure(void *data, void *userdata)
316
{
317 318 319 320 321
	struct eeh_dev *edev = (struct eeh_dev *)data;
	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
	struct pci_driver *driver;

	if (!dev) return NULL;
322 323
	dev->error_state = pci_channel_io_perm_failure;

324 325
	driver = eeh_pcid_get(dev);
	if (!driver) return NULL;
326

327 328 329
	eeh_disable_irq(dev);

	if (!driver->err_handler ||
330 331
	    !driver->err_handler->error_detected) {
		eeh_pcid_put(dev);
332
		return NULL;
333
	}
334

335
	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
336

337
	eeh_pcid_put(dev);
338
	return NULL;
339 340 341
}

/**
342
 * eeh_reset_device - Perform actual reset of a pci slot
343
 * @pe: EEH PE
344
 * @bus: PCI bus corresponding to the isolcated slot
345
 *
346 347 348
 * This routine must be called to do reset on the indicated PE.
 * During the reset, udev might be invoked because those affected
 * PCI devices will be removed and then added.
349
 */
350
static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
351
{
352
	struct timeval tstamp;
353 354 355
	int cnt, rc;

	/* pcibios will clear the counter; save the value */
356
	cnt = pe->freeze_count;
357
	tstamp = pe->tstamp;
358

359 360 361 362 363 364
	/*
	 * We don't remove the corresponding PE instances because
	 * we need the information afterwords. The attached EEH
	 * devices are expected to be attached soon when calling
	 * into pcibios_add_pci_devices().
	 */
G
Gavin Shan 已提交
365 366 367 368
	if (bus) {
		eeh_pe_state_mark(pe, EEH_PE_KEEP);
		pcibios_remove_pci_devices(bus);
	}
369 370

	/* Reset the pci controller. (Asserts RST#; resets config space).
371
	 * Reconfigure bridges and devices. Don't try to bring the system
372 373
	 * up if the reset failed for some reason.
	 */
374
	rc = eeh_reset_pe(pe);
375 376
	if (rc)
		return rc;
377

378 379 380
	/* Restore PE */
	eeh_ops->configure_bridge(pe);
	eeh_pe_restore_bars(pe);
381 382

	/* Give the system 5 seconds to finish running the user-space
G
Gavin Shan 已提交
383 384 385
	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes,
	 * this is a hack, but if we don't do this, and try to bring
	 * the device up before the scripts have taken it down,
386 387 388
	 * potentially weird things happen.
	 */
	if (bus) {
389
		ssleep(5);
390
		pcibios_add_pci_devices(bus);
G
Gavin Shan 已提交
391
		eeh_pe_state_clear(pe, EEH_PE_KEEP);
392
	}
393 394

	pe->tstamp = tstamp;
395
	pe->freeze_count = cnt;
396 397

	return 0;
398 399 400 401 402
}

/* The longest amount of time to wait for a pci device
 * to come back on line, in seconds.
 */
403
#define MAX_WAIT_FOR_RECOVERY 150
404

405
static void eeh_handle_normal_event(struct eeh_pe *pe)
406 407
{
	struct pci_bus *frozen_bus;
408
	int rc = 0;
409
	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
410

411
	frozen_bus = eeh_pe_bus_get(pe);
412
	if (!frozen_bus) {
413 414 415
		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
			__func__, pe->phb->global_number, pe->addr);
		return;
416 417
	}

418
	eeh_pe_update_time_stamp(pe);
419 420
	pe->freeze_count++;
	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
421
		goto excess_failures;
422 423
	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
		pe->freeze_count);
424 425 426 427 428 429 430

	/* Walk the various device drivers attached to this slot through
	 * a reset sequence, giving each an opportunity to do what it needs
	 * to accomplish the reset.  Each child gets a report of the
	 * status ... if any child can't handle the reset, then the entire
	 * slot is dlpar removed and added.
	 */
431
	pr_info("EEH: Notify device drivers to shutdown\n");
432
	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
433

434
	/* Get the current PCI slot state. This can take a long time,
435 436
	 * sometimes over 3 seconds for certain systems.
	 */
437
	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
438
	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
439
		pr_warning("EEH: Permanent failure\n");
440 441 442
		goto hard_fail;
	}

443 444
	/* Since rtas may enable MMIO when posting the error log,
	 * don't post the error log until after all dev drivers
445 446
	 * have been informed.
	 */
447
	pr_info("EEH: Collect temporary log\n");
448
	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
449

450 451 452 453
	/* If all device drivers were EEH-unaware, then shut
	 * down all of the device drivers, and hope they
	 * go down willingly, without panicing the system.
	 */
454
	if (result == PCI_ERS_RESULT_NONE) {
455
		pr_info("EEH: Reset with hotplug activity\n");
456
		rc = eeh_reset_device(pe, frozen_bus);
457
		if (rc) {
458 459
			pr_warning("%s: Unable to reset, err=%d\n",
				   __func__, rc);
460
			goto hard_fail;
461
		}
462 463
	}

464 465
	/* If all devices reported they can proceed, then re-enable MMIO */
	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
466
		pr_info("EEH: Enable I/O for affected devices\n");
467
		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
468

469 470
		if (rc < 0)
			goto hard_fail;
471 472 473
		if (rc) {
			result = PCI_ERS_RESULT_NEED_RESET;
		} else {
474
			pr_info("EEH: Notify device drivers to resume I/O\n");
475
			result = PCI_ERS_RESULT_NONE;
476
			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
477
		}
478 479
	}

480
	/* If all devices reported they can proceed, then re-enable DMA */
481
	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
482
		pr_info("EEH: Enabled DMA for affected devices\n");
483
		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
484

485 486
		if (rc < 0)
			goto hard_fail;
487 488
		if (rc)
			result = PCI_ERS_RESULT_NEED_RESET;
L
Linas Vepstas 已提交
489 490
		else
			result = PCI_ERS_RESULT_RECOVERED;
491 492 493
	}

	/* If any device has a hard failure, then shut off everything. */
494
	if (result == PCI_ERS_RESULT_DISCONNECT) {
495
		pr_warning("EEH: Device driver gave up\n");
496
		goto hard_fail;
497
	}
498 499 500

	/* If any device called out for a reset, then reset the slot */
	if (result == PCI_ERS_RESULT_NEED_RESET) {
501
		pr_info("EEH: Reset without hotplug activity\n");
502
		rc = eeh_reset_device(pe, NULL);
503
		if (rc) {
504 505
			pr_warning("%s: Cannot reset, err=%d\n",
				   __func__, rc);
506
			goto hard_fail;
507
		}
508 509 510

		pr_info("EEH: Notify device drivers "
			"the completion of reset\n");
511
		result = PCI_ERS_RESULT_NONE;
512
		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
513 514
	}

515
	/* All devices should claim they have recovered by now. */
516 517
	if ((result != PCI_ERS_RESULT_RECOVERED) &&
	    (result != PCI_ERS_RESULT_NONE)) {
518
		pr_warning("EEH: Not recovered\n");
519
		goto hard_fail;
520
	}
521

522
	/* Tell all device drivers that they can resume operations */
523
	pr_info("EEH: Notify device driver to resume\n");
524
	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
525

526
	return;
G
Gavin Shan 已提交
527

528
excess_failures:
529 530 531 532 533
	/*
	 * About 90% of all real-life EEH failures in the field
	 * are due to poorly seated PCI cards. Only 10% or so are
	 * due to actual, failed cards.
	 */
534 535 536 537 538
	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
	       "last hour and has been permanently disabled.\n"
	       "Please try reseating or replacing it.\n",
		pe->phb->global_number, pe->addr,
		pe->freeze_count);
539 540 541
	goto perm_error;

hard_fail:
542 543 544
	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
	       "Please try reseating or replacing it\n",
		pe->phb->global_number, pe->addr);
545

546
perm_error:
547
	eeh_slot_error_detail(pe, EEH_LOG_PERM);
548 549

	/* Notify all devices that they're about to go down. */
550
	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
551 552

	/* Shut down the device drivers for good. */
553 554
	if (frozen_bus)
		pcibios_remove_pci_devices(frozen_bus);
555
}
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664

static void eeh_handle_special_event(void)
{
	struct eeh_pe *pe, *phb_pe;
	struct pci_bus *bus;
	struct pci_controller *hose, *tmp;
	unsigned long flags;
	int rc = 0;

	/*
	 * The return value from next_error() has been classified as follows.
	 * It might be good to enumerate them. However, next_error() is only
	 * supported by PowerNV platform for now. So it would be fine to use
	 * integer directly:
	 *
	 * 4 - Dead IOC           3 - Dead PHB
	 * 2 - Fenced PHB         1 - Frozen PE
	 * 0 - No error found
	 *
	 */
	rc = eeh_ops->next_error(&pe);
	if (rc <= 0)
		return;

	switch (rc) {
	case 4:
		/* Mark all PHBs in dead state */
		eeh_serialize_lock(&flags);
		list_for_each_entry_safe(hose, tmp,
				&hose_list, list_node) {
			phb_pe = eeh_phb_pe_get(hose);
			if (!phb_pe) continue;

			eeh_pe_state_mark(phb_pe,
				EEH_PE_ISOLATED | EEH_PE_PHB_DEAD);
		}
		eeh_serialize_unlock(flags);

		/* Purge all events */
		eeh_remove_event(NULL);
		break;
	case 3:
	case 2:
	case 1:
		/* Mark the PE in fenced state */
		eeh_serialize_lock(&flags);
		if (rc == 3)
			eeh_pe_state_mark(pe,
				EEH_PE_ISOLATED | EEH_PE_PHB_DEAD);
		else
			eeh_pe_state_mark(pe,
				EEH_PE_ISOLATED | EEH_PE_RECOVERING);
		eeh_serialize_unlock(flags);

		/* Purge all events of the PHB */
		eeh_remove_event(pe);
		break;
	default:
		pr_err("%s: Invalid value %d from next_error()\n",
		       __func__, rc);
		return;
	}

	/*
	 * For fenced PHB and frozen PE, it's handled as normal
	 * event. We have to remove the affected PHBs for dead
	 * PHB and IOC
	 */
	if (rc == 2 || rc == 1)
		eeh_handle_normal_event(pe);
	else {
		list_for_each_entry_safe(hose, tmp,
			&hose_list, list_node) {
			phb_pe = eeh_phb_pe_get(hose);
			if (!phb_pe || !(phb_pe->state & EEH_PE_PHB_DEAD))
				continue;

			bus = eeh_pe_bus_get(phb_pe);
			/* Notify all devices that they're about to go down. */
			eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
			pcibios_remove_pci_devices(bus);
		}
	}
}

/**
 * eeh_handle_event - Reset a PCI device after hard lockup.
 * @pe: EEH PE
 *
 * While PHB detects address or data parity errors on particular PCI
 * slot, the associated PE will be frozen. Besides, DMA's occurring
 * to wild addresses (which usually happen due to bugs in device
 * drivers or in PCI adapter firmware) can cause EEH error. #SERR,
 * #PERR or other misc PCI-related errors also can trigger EEH errors.
 *
 * Recovery process consists of unplugging the device driver (which
 * generated hotplug events to userspace), then issuing a PCI #RST to
 * the device, then reconfiguring the PCI config space for all bridges
 * & devices under this slot, and then finally restarting the device
 * drivers (which cause a second set of hotplug events to go out to
 * userspace).
 */
void eeh_handle_event(struct eeh_pe *pe)
{
	if (pe)
		eeh_handle_normal_event(pe);
	else
		eeh_handle_special_event();
}