aerdrv_core.c 19.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * drivers/pci/pcie/aer/aerdrv_core.c
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * This file implements the core part of PCI-Express AER. When an pci-express
 * error is delivered, an error message will be collected and printed to
 * console, then, an error recovery procedure will be executed by following
 * the pci error recovery rules.
 *
 * Copyright (C) 2006 Intel Corp.
 *	Tom Long Nguyen (tom.l.nguyen@intel.com)
 *	Zhang Yanmin (yanmin.zhang@intel.com)
 *
 */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/suspend.h>
#include <linux/delay.h>
26
#include <linux/slab.h>
27 28 29
#include "aerdrv.h"

static int forceload;
30
static int nosourceid;
31
module_param(forceload, bool, 0);
32
module_param(nosourceid, bool, 0);
33 34 35 36 37 38

int pci_enable_pcie_error_reporting(struct pci_dev *dev)
{
	u16 reg16 = 0;
	int pos;

39 40 41
	if (dev->aer_firmware_first)
		return -EIO;

Y
Yu Zhao 已提交
42
	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
43 44 45
	if (!pos)
		return -EIO;

K
Kenji Kaneshige 已提交
46
	pos = pci_pcie_cap(dev);
47 48 49
	if (!pos)
		return -EIO;

50 51 52 53 54 55
	pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
	reg16 = reg16 |
		PCI_EXP_DEVCTL_CERE |
		PCI_EXP_DEVCTL_NFERE |
		PCI_EXP_DEVCTL_FERE |
		PCI_EXP_DEVCTL_URRE;
56 57
	pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16);

58 59
	return 0;
}
60
EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
61 62 63 64 65 66

int pci_disable_pcie_error_reporting(struct pci_dev *dev)
{
	u16 reg16 = 0;
	int pos;

67 68 69
	if (dev->aer_firmware_first)
		return -EIO;

K
Kenji Kaneshige 已提交
70
	pos = pci_pcie_cap(dev);
71 72 73 74 75 76 77 78
	if (!pos)
		return -EIO;

	pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
	reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE |
			PCI_EXP_DEVCTL_NFERE |
			PCI_EXP_DEVCTL_FERE |
			PCI_EXP_DEVCTL_URRE);
79 80
	pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16);

81 82
	return 0;
}
83
EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
84 85 86 87

int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
{
	int pos;
88
	u32 status;
89

90
	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
91 92 93 94
	if (!pos)
		return -EIO;

	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
95 96
	if (status)
		pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
97 98 99

	return 0;
}
100
EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
101

102 103
static inline int compare_device_id(struct pci_dev *dev,
			struct aer_err_info *e_info)
104
{
105 106 107 108 109 110
	if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) {
		/*
		 * Device ID match
		 */
		return 1;
	}
111

112 113 114
	return 0;
}

Z
Zhang, Yanmin 已提交
115 116 117 118 119 120
static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
{
	if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
		e_info->dev[e_info->error_dev_num] = dev;
		e_info->error_dev_num++;
		return 1;
121 122 123
	}

	return 0;
Z
Zhang, Yanmin 已提交
124 125 126
}


127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
#define	PCI_BUS(x)	(((x) >> 8) & 0xff)

static int find_device_iter(struct pci_dev *dev, void *data)
{
	int pos;
	u32 status;
	u32 mask;
	u16 reg16;
	int result;
	struct aer_err_info *e_info = (struct aer_err_info *)data;

	/*
	 * When bus id is equal to 0, it might be a bad id
	 * reported by root port.
	 */
	if (!nosourceid && (PCI_BUS(e_info->id) != 0)) {
		result = compare_device_id(dev, e_info);
		if (result)
Z
Zhang, Yanmin 已提交
145 146 147 148 149 150
			add_error_device(e_info, dev);

		/*
		 * If there is no multiple error, we stop
		 * or continue based on the id comparing.
		 */
H
Hidetoshi Seto 已提交
151
		if (!e_info->multi_error_valid)
Z
Zhang, Yanmin 已提交
152 153 154 155 156 157 158 159 160
			return result;

		/*
		 * If there are multiple errors and id does match,
		 * We need continue to search other devices under
		 * the root port. Return 0 means that.
		 */
		if (result)
			return 0;
161 162 163
	}

	/*
Z
Zhang, Yanmin 已提交
164 165 166 167 168 169
	 * When either
	 *      1) nosourceid==y;
	 *      2) bus id is equal to 0. Some ports might lose the bus
	 *              id of error source id;
	 *      3) There are multiple errors and prior id comparing fails;
	 * We check AER status registers to find the initial reporter.
170 171 172
	 */
	if (atomic_read(&dev->enable_cnt) == 0)
		return 0;
K
Kenji Kaneshige 已提交
173
	pos = pci_pcie_cap(dev);
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	if (!pos)
		return 0;
	/* Check if AER is enabled */
	pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
	if (!(reg16 & (
		PCI_EXP_DEVCTL_CERE |
		PCI_EXP_DEVCTL_NFERE |
		PCI_EXP_DEVCTL_FERE |
		PCI_EXP_DEVCTL_URRE)))
		return 0;
	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
	if (!pos)
		return 0;

	status = 0;
	mask = 0;
	if (e_info->severity == AER_CORRECTABLE) {
191 192 193
		pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
		pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask);
		if (status & ~mask) {
Z
Zhang, Yanmin 已提交
194 195
			add_error_device(e_info, dev);
			goto added;
196
		}
197
	} else {
198 199 200
		pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
		pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask);
		if (status & ~mask) {
Z
Zhang, Yanmin 已提交
201 202
			add_error_device(e_info, dev);
			goto added;
203 204 205 206
		}
	}

	return 0;
Z
Zhang, Yanmin 已提交
207 208

added:
H
Hidetoshi Seto 已提交
209
	if (e_info->multi_error_valid)
Z
Zhang, Yanmin 已提交
210 211 212
		return 0;
	else
		return 1;
213 214 215 216
}

/**
 * find_source_device - search through device hierarchy for source device
R
Randy Dunlap 已提交
217
 * @parent: pointer to Root Port pci_dev data structure
218
 * @e_info: including detailed error information such like id
219
 *
220 221 222
 * Return true if found.
 *
 * Invoked by DPC when error is detected at the Root Port.
R
Randy Dunlap 已提交
223
 */
224
static bool find_source_device(struct pci_dev *parent,
225
		struct aer_err_info *e_info)
226 227
{
	struct pci_dev *dev = parent;
228
	int result;
229 230

	/* Is Root Port an agent that sends error message? */
231 232
	result = find_device_iter(dev, e_info);
	if (result)
233
		return true;
234

235
	pci_walk_bus(parent->subordinate, find_device_iter, e_info);
236 237 238 239 240 241 242 243

	if (!e_info->error_dev_num) {
		dev_printk(KERN_DEBUG, &parent->dev,
				"can't find device of ID%04x\n",
				e_info->id);
		return false;
	}
	return true;
244 245
}

246
static int report_error_detected(struct pci_dev *dev, void *data)
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
{
	pci_ers_result_t vote;
	struct pci_error_handlers *err_handler;
	struct aer_broadcast_data *result_data;
	result_data = (struct aer_broadcast_data *) data;

	dev->error_state = result_data->state;

	if (!dev->driver ||
		!dev->driver->err_handler ||
		!dev->driver->err_handler->error_detected) {
		if (result_data->state == pci_channel_io_frozen &&
			!(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) {
			/*
			 * In case of fatal recovery, if one of down-
			 * stream device has no driver. We might be
			 * unable to recover because a later insmod
			 * of a driver for this device is unaware of
			 * its hw state.
			 */
267 268 269
			dev_printk(KERN_DEBUG, &dev->dev, "device has %s\n",
				   dev->driver ?
				   "no AER-aware driver" : "no driver");
270
		}
271
		return 0;
272 273 274 275 276
	}

	err_handler = dev->driver->err_handler;
	vote = err_handler->error_detected(dev, result_data->state);
	result_data->result = merge_result(result_data->result, vote);
277
	return 0;
278 279
}

280
static int report_mmio_enabled(struct pci_dev *dev, void *data)
281 282 283 284 285 286 287 288 289
{
	pci_ers_result_t vote;
	struct pci_error_handlers *err_handler;
	struct aer_broadcast_data *result_data;
	result_data = (struct aer_broadcast_data *) data;

	if (!dev->driver ||
		!dev->driver->err_handler ||
		!dev->driver->err_handler->mmio_enabled)
290
		return 0;
291 292 293 294

	err_handler = dev->driver->err_handler;
	vote = err_handler->mmio_enabled(dev);
	result_data->result = merge_result(result_data->result, vote);
295
	return 0;
296 297
}

298
static int report_slot_reset(struct pci_dev *dev, void *data)
299 300 301 302 303 304 305 306 307
{
	pci_ers_result_t vote;
	struct pci_error_handlers *err_handler;
	struct aer_broadcast_data *result_data;
	result_data = (struct aer_broadcast_data *) data;

	if (!dev->driver ||
		!dev->driver->err_handler ||
		!dev->driver->err_handler->slot_reset)
308
		return 0;
309 310 311 312

	err_handler = dev->driver->err_handler;
	vote = err_handler->slot_reset(dev);
	result_data->result = merge_result(result_data->result, vote);
313
	return 0;
314 315
}

316
static int report_resume(struct pci_dev *dev, void *data)
317 318 319 320 321 322 323
{
	struct pci_error_handlers *err_handler;

	dev->error_state = pci_channel_io_normal;

	if (!dev->driver ||
		!dev->driver->err_handler ||
H
Hidetoshi Seto 已提交
324
		!dev->driver->err_handler->resume)
325
		return 0;
326 327 328

	err_handler = dev->driver->err_handler;
	err_handler->resume(dev);
329
	return 0;
330 331 332 333
}

/**
 * broadcast_error_message - handle message broadcast to downstream drivers
R
Randy Dunlap 已提交
334
 * @dev: pointer to from where in a hierarchy message is broadcasted down
335
 * @state: error state
R
Randy Dunlap 已提交
336 337
 * @error_mesg: message to print
 * @cb: callback to be broadcasted
338 339 340 341
 *
 * Invoked during error recovery process. Once being invoked, the content
 * of error severity will be broadcasted to all downstream drivers in a
 * hierarchy in question.
R
Randy Dunlap 已提交
342
 */
343 344 345
static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
	enum pci_channel_state state,
	char *error_mesg,
346
	int (*cb)(struct pci_dev *, void *))
347 348 349
{
	struct aer_broadcast_data result_data;

350
	dev_printk(KERN_DEBUG, &dev->dev, "broadcast %s message\n", error_mesg);
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
	result_data.state = state;
	if (cb == report_error_detected)
		result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
	else
		result_data.result = PCI_ERS_RESULT_RECOVERED;

	if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
		/*
		 * If the error is reported by a bridge, we think this error
		 * is related to the downstream link of the bridge, so we
		 * do error recovery on all subordinates of the bridge instead
		 * of the bridge and clear the error status of the bridge.
		 */
		if (cb == report_error_detected)
			dev->error_state = state;
		pci_walk_bus(dev->subordinate, cb, &result_data);
		if (cb == report_resume) {
			pci_cleanup_aer_uncorrect_error_status(dev);
			dev->error_state = pci_channel_io_normal;
		}
371
	} else {
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
		/*
		 * If the error is reported by an end point, we think this
		 * error is related to the upstream link of the end point.
		 */
		pci_walk_bus(dev->bus, cb, &result_data);
	}

	return result_data.result;
}

struct find_aer_service_data {
	struct pcie_port_service_driver *aer_driver;
	int is_downstream;
};

static int find_aer_service_iter(struct device *device, void *data)
{
	struct device_driver *driver;
	struct pcie_port_service_driver *service_driver;
	struct find_aer_service_data *result;

	result = (struct find_aer_service_data *) data;

	if (device->bus == &pcie_port_bus_type) {
396
		struct pcie_device *pcie = to_pcie_device(device);
397

398
		if (pcie->port->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
399 400 401 402 403
			result->is_downstream = 1;

		driver = device->driver;
		if (driver) {
			service_driver = to_service_driver(driver);
404
			if (service_driver->service == PCIE_PORT_SERVICE_AER) {
405 406 407 408 409 410 411 412 413 414 415 416
				result->aer_driver = service_driver;
				return 1;
			}
		}
	}

	return 0;
}

static void find_aer_service(struct pci_dev *dev,
		struct find_aer_service_data *data)
{
417 418
	int retval;
	retval = device_for_each_child(&dev->dev, data, find_aer_service_iter);
419 420 421 422 423 424 425 426 427 428 429 430
}

static pci_ers_result_t reset_link(struct pcie_device *aerdev,
		struct pci_dev *dev)
{
	struct pci_dev *udev;
	pci_ers_result_t status;
	struct find_aer_service_data data;

	if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
		udev = dev;
	else
431
		udev = dev->bus->self;
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

	data.is_downstream = 0;
	data.aer_driver = NULL;
	find_aer_service(udev, &data);

	/*
	 * Use the aer driver of the error agent firstly.
	 * If it hasn't the aer driver, use the root port's
	 */
	if (!data.aer_driver || !data.aer_driver->reset_link) {
		if (data.is_downstream &&
			aerdev->device.driver &&
			to_service_driver(aerdev->device.driver)->reset_link) {
			data.aer_driver =
				to_service_driver(aerdev->device.driver);
		} else {
448 449
			dev_printk(KERN_DEBUG, &dev->dev, "no link-reset "
				   "support\n");
450 451 452 453 454 455
			return PCI_ERS_RESULT_DISCONNECT;
		}
	}

	status = data.aer_driver->reset_link(udev);
	if (status != PCI_ERS_RESULT_RECOVERED) {
456 457
		dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream "
			   "device %s failed\n", pci_name(udev));
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
		return PCI_ERS_RESULT_DISCONNECT;
	}

	return status;
}

/**
 * do_recovery - handle nonfatal/fatal error recovery process
 * @aerdev: pointer to a pcie_device data structure of root port
 * @dev: pointer to a pci_dev data structure of agent detecting an error
 * @severity: error severity type
 *
 * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
 * error detected message to all downstream drivers within a hierarchy in
 * question and return the returned code.
R
Randy Dunlap 已提交
473
 */
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
		struct pci_dev *dev,
		int severity)
{
	pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
	enum pci_channel_state state;

	if (severity == AER_FATAL)
		state = pci_channel_io_frozen;
	else
		state = pci_channel_io_normal;

	status = broadcast_error_message(dev,
			state,
			"error_detected",
			report_error_detected);

	if (severity == AER_FATAL) {
		result = reset_link(aerdev, dev);
		if (result != PCI_ERS_RESULT_RECOVERED) {
			/* TODO: Should panic here? */
			return result;
		}
	}

	if (status == PCI_ERS_RESULT_CAN_RECOVER)
		status = broadcast_error_message(dev,
				state,
				"mmio_enabled",
				report_mmio_enabled);

	if (status == PCI_ERS_RESULT_NEED_RESET) {
		/*
		 * TODO: Should call platform-specific
		 * functions to reset slot before calling
		 * drivers' slot_reset callbacks?
		 */
		status = broadcast_error_message(dev,
				state,
				"slot_reset",
				report_slot_reset);
	}

	if (status == PCI_ERS_RESULT_RECOVERED)
		broadcast_error_message(dev,
				state,
				"resume",
				report_resume);

	return status;
}

/**
 * handle_error_source - handle logging error into an event log
 * @aerdev: pointer to pcie_device data structure of the root port
 * @dev: pointer to pci_dev data structure of error source device
 * @info: comprehensive error information
 *
 * Invoked when an error being detected by Root Port.
R
Randy Dunlap 已提交
533
 */
534
static void handle_error_source(struct pcie_device *aerdev,
535
	struct pci_dev *dev,
536
	struct aer_err_info *info)
537 538 539 540
{
	pci_ers_result_t status = 0;
	int pos;

541
	if (info->severity == AER_CORRECTABLE) {
542 543 544 545
		/*
		 * Correctable error does not need software intevention.
		 * No need to go through error recovery process.
		 */
546
		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
547 548
		if (pos)
			pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
549
					info->status);
550
	} else {
551
		status = do_recovery(aerdev, dev, info->severity);
552
		if (status == PCI_ERS_RESULT_RECOVERED) {
553 554
			dev_printk(KERN_DEBUG, &dev->dev, "AER driver "
				   "successfully recovered\n");
555 556
		} else {
			/* TODO: Should kernel panic here? */
557 558
			dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't "
				   "recover\n");
559 560 561 562 563 564 565 566 567
		}
	}
}

/**
 * get_e_source - retrieve an error source
 * @rpc: pointer to the root port which holds an error
 *
 * Invoked by DPC handler to consume an error.
R
Randy Dunlap 已提交
568
 */
569
static struct aer_err_source *get_e_source(struct aer_rpc *rpc)
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
{
	struct aer_err_source *e_source;
	unsigned long flags;

	/* Lock access to Root error producer/consumer index */
	spin_lock_irqsave(&rpc->e_lock, flags);
	if (rpc->prod_idx == rpc->cons_idx) {
		spin_unlock_irqrestore(&rpc->e_lock, flags);
		return NULL;
	}
	e_source = &rpc->e_sources[rpc->cons_idx];
	rpc->cons_idx++;
	if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
		rpc->cons_idx = 0;
	spin_unlock_irqrestore(&rpc->e_lock, flags);

	return e_source;
}

589 590 591 592 593 594 595
/**
 * get_device_error_info - read error status from dev and store it to info
 * @dev: pointer to the device expected to have a error record
 * @info: pointer to structure to store the error record
 *
 * Return 1 on success, 0 on error.
 */
596 597
static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
{
598
	int pos, temp;
599

600
	info->status = 0;
H
Hidetoshi Seto 已提交
601
	info->tlp_header_valid = 0;
602

603
	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
604 605 606

	/* The device might not support AER */
	if (!pos)
607
		return 1;
608 609 610 611

	if (info->severity == AER_CORRECTABLE) {
		pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
			&info->status);
612 613 614
		pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK,
			&info->mask);
		if (!(info->status & ~info->mask))
615
			return 0;
616 617 618 619 620 621
	} else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
		info->severity == AER_NONFATAL) {

		/* Link is still healthy for IO reads */
		pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
			&info->status);
622 623 624
		pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK,
			&info->mask);
		if (!(info->status & ~info->mask))
625
			return 0;
626

627 628
		/* Get First Error Pointer */
		pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp);
H
Hidetoshi Seto 已提交
629
		info->first_error = PCI_ERR_CAP_FEP(temp);
630

631
		if (info->status & AER_LOG_TLP_MASKS) {
H
Hidetoshi Seto 已提交
632
			info->tlp_header_valid = 1;
633 634 635 636 637 638 639 640 641 642 643
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
			pci_read_config_dword(dev,
				pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
		}
	}

644
	return 1;
645 646
}

Z
Zhang, Yanmin 已提交
647 648 649 650 651
static inline void aer_process_err_devices(struct pcie_device *p_device,
			struct aer_err_info *e_info)
{
	int i;

652
	/* Report all before handle them, not to lost records by reset etc. */
Z
Zhang, Yanmin 已提交
653
	for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
654
		if (get_device_error_info(e_info->dev[i], e_info))
Z
Zhang, Yanmin 已提交
655
			aer_print_error(e_info->dev[i], e_info);
656 657 658 659
	}
	for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
		if (get_device_error_info(e_info->dev[i], e_info))
			handle_error_source(p_device, e_info->dev[i], e_info);
Z
Zhang, Yanmin 已提交
660 661 662
	}
}

663 664 665 666
/**
 * aer_isr_one_error - consume an error detected by root port
 * @p_device: pointer to error root port service device
 * @e_src: pointer to an error source
R
Randy Dunlap 已提交
667
 */
668 669 670
static void aer_isr_one_error(struct pcie_device *p_device,
		struct aer_err_source *e_src)
{
671
	struct aer_err_info *e_info;
672
	int i;
673 674 675 676 677 678 679 680

	/* struct aer_err_info might be big, so we allocate it with slab */
	e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL);
	if (e_info == NULL) {
		dev_printk(KERN_DEBUG, &p_device->port->dev,
			"Can't allocate mem when processing AER errors\n");
		return;
	}
681 682 683 684 685 686 687 688 689 690 691

	/*
	 * There is a possibility that both correctable error and
	 * uncorrectable error being logged. Report correctable error first.
	 */
	for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) {
		if (i > 4)
			break;
		if (!(e_src->status & i))
			continue;

692 693
		memset(e_info, 0, sizeof(struct aer_err_info));

694 695
		/* Init comprehensive error information */
		if (i & PCI_ERR_ROOT_COR_RCV) {
696 697
			e_info->id = ERR_COR_ID(e_src->id);
			e_info->severity = AER_CORRECTABLE;
698
		} else {
699 700
			e_info->id = ERR_UNCOR_ID(e_src->id);
			e_info->severity = ((e_src->status >> 6) & 1);
701 702 703 704
		}
		if (e_src->status &
			(PCI_ERR_ROOT_MULTI_COR_RCV |
			 PCI_ERR_ROOT_MULTI_UNCOR_RCV))
H
Hidetoshi Seto 已提交
705
			e_info->multi_error_valid = 1;
706

707 708
		aer_print_port_info(p_device->port, e_info);

709 710
		if (find_source_device(p_device->port, e_info))
			aer_process_err_devices(p_device, e_info);
711
	}
712 713

	kfree(e_info);
714 715 716 717
}

/**
 * aer_isr - consume errors detected by root port
718
 * @work: definition of this work item
719 720
 *
 * Invoked, as DPC, when root port records new detected error
R
Randy Dunlap 已提交
721
 */
722
void aer_isr(struct work_struct *work)
723
{
724 725
	struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
	struct pcie_device *p_device = rpc->rpd;
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
	struct aer_err_source *e_src;

	mutex_lock(&rpc->rpc_mutex);
	e_src = get_e_source(rpc);
	while (e_src) {
		aer_isr_one_error(p_device, e_src);
		e_src = get_e_source(rpc);
	}
	mutex_unlock(&rpc->rpc_mutex);

	wake_up(&rpc->wait_release);
}

/**
 * aer_init - provide AER initialization
 * @dev: pointer to AER pcie device
 *
 * Invoked when AER service driver is loaded.
R
Randy Dunlap 已提交
744
 */
745 746
int aer_init(struct pcie_device *dev)
{
747 748 749 750 751 752 753 754
	if (dev->port->aer_firmware_first) {
		dev_printk(KERN_DEBUG, &dev->device,
			   "PCIe errors handled by platform firmware.\n");
		goto out;
	}

	if (aer_osc_setup(dev))
		goto out;
755

756
	return 0;
757 758 759 760 761 762 763 764
out:
	if (forceload) {
		dev_printk(KERN_DEBUG, &dev->device,
			   "aerdrv forceload requested.\n");
		dev->port->aer_firmware_first = 0;
		return 0;
	}
	return -ENXIO;
765
}