[PATCH 5/6] PCI Express Advanced Error Reporting Driver

From: long
Date: Fri Mar 11 2005 - 18:41:03 EST


This patch includes the source code of core component of PCI Express
Advanced Error Reporting driver.

Signed-off-by: T. Long Nguyen <tom.l.nguyen@xxxxxxxxx>

--------------------------------------------------------------------
diff -urpN linux-2.6.11-rc5/drivers/pci/pcie/aer/aerdrv_core.c patch-2.6.11-rc5-aerc3-split5/drivers/pci/pcie/aer/aerdrv_core.c
--- linux-2.6.11-rc5/drivers/pci/pcie/aer/aerdrv_core.c 1969-12-31 19:00:00.000000000 -0500
+++ patch-2.6.11-rc5-aerc3-split5/drivers/pci/pcie/aer/aerdrv_core.c 2005-03-10 10:31:09.000000000 -0500
@@ -0,0 +1,911 @@
+/*
+ * Copyright (C) 2005 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@xxxxxxxxx)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/rtc.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include "aerdrv.h"
+
+LIST_HEAD(rc_list); /* Define Root Complex List */
+static int verbose = VERBOSE_LIMIT_DISPLAY;
+static int auto_mode = 1;
+static int aer_init_flag = 0;
+
+/**
+ * print_source_devices - print a list of AER devices in Root Port hierarchy
+ * @head: pointer to Root Port's children list
+ * @page: pointer to a buffer
+ *
+ * Invoked by user interface status. Caller must acquire Root semaphore prior
+ * to calling.
+ **/
+static int print_source_devices(struct list_head *head, char *page)
+{
+ struct list_head *entry;
+ struct aer_device *dev;
+ char *p = page;
+ char *error_name;
+
+ list_for_each(entry, head) {
+ dev = container_of(entry, struct aer_device, node);
+ p += sprintf(p, " Bus %d, device %d, function %d:\n",
+ AER_DEVICE_BUS(dev->requestor_id),
+ AER_DEVICE_DEV(dev->requestor_id),
+ AER_DEVICE_FUNC(dev->requestor_id));
+ if (dev->handle) {
+ p += sprintf(p, " Class %04x: PCI device %02x:%02x\n",
+ dev->class_code, dev->device, dev->vendor);
+ p += sprintf(p, " COR_ERR %d, NONFATAL_ERR %d,"
+ " FATAL_ERR %d\n",
+ dev->correctables,
+ dev->nonfatals,
+ dev->fatals);
+ } else
+ p += sprintf(p, " COR_ERR %d, UNCOR_ERR %d\n",
+ dev->correctables,
+ dev->uncorrectables);
+ error_name = aer_get_error_source_name(&dev->last_recorded_err);
+ if (error_name) {
+ p += sprintf(p, " Last Error Detected @ "
+ "%d/%d/%d %d:%d:%d - %s\n",
+ dev->time_stamp.month, dev->time_stamp.day,
+ dev->time_stamp.year + 1900, dev->time_stamp.hours,
+ dev->time_stamp.minutes, dev->time_stamp.seconds,
+ error_name);
+ }
+ p += sprintf(p, " Register Callbacks - %s\n",
+ (dev->handle != NULL) ? "Yes" : "No");
+ if (PCIE_PORT(dev->attribute.type))
+ p += print_source_devices(&dev->children, p);
+ }
+
+ return p-page;
+}
+
+/**
+ * search_node - search for a Root Port node associated with this
+ * downstream ID
+ * @requestor_id: endpoint source ID
+ *
+ * Invoked to search for an associated Root Port node.
+ * A rc_list list, initialized during driver init, contains physical
+ * Root Port devices in Root Complex, no semaphore access lock is
+ * necessary prior to calling.
+ **/
+static struct aer_rpc* search_node(unsigned short requestor_id)
+{
+ struct list_head *entry;
+ struct aer_rpc *rpc = NULL;
+ int bus = AER_DEVICE_BUS(requestor_id);
+
+ if (!list_empty(&rc_list)) {
+ struct aer_rpc *tmp;
+
+ list_for_each(entry, &rc_list) {
+ tmp = container_of(entry, struct aer_rpc, node);
+ if ((bus >= tmp->secondary && bus <= tmp->subordinate)||
+ tmp->self_id == requestor_id) {
+ rpc = tmp;
+ break;
+ }
+ }
+ }
+
+ return rpc;
+}
+
+/**
+ * search_direct_parent - search for a direct PCIE Port for endpoint
+ * @head: pointer to a Root Port's children list
+ * @id: endpoint source ID
+ *
+ * Invoked to search for a direct one-to-one PCIE Port associated with
+ * endpoint. Caller must acquire Root semaphore "rpc_sema" prior to
+ * calling.
+ **/
+static struct aer_device* search_direct_parent(struct list_head *head, u16 id)
+{
+ struct list_head *entry;
+ struct aer_device *parent = NULL;
+
+ if (list_empty(head))
+ return NULL;
+
+ list_for_each(entry, head) {
+ struct aer_device *dev;
+ int secondary, subordinate, bus = AER_DEVICE_BUS(id);
+
+ dev = container_of(entry, struct aer_device, node);
+ if (!PCIE_PORT(dev->attribute.type))
+ continue;
+
+ secondary = dev->attribute.p2p.bus.secondary;
+ subordinate = dev->attribute.p2p.bus.subordinate;
+ if (!(bus >= secondary && bus <= subordinate))
+ continue;
+
+ if (!(parent = search_direct_parent(&dev->children, id)))
+ parent = dev;
+ break;
+ }
+
+ return parent;
+}
+
+/**
+ * free_source_devices - free all children under Root Port
+ * @head: pointer to a Root Port's children list
+ *
+ * Invoked when AER Root service is unloaded.
+ * Caller must acquire Root semaphore "rpc_sema" prior to calling.
+ **/
+static void free_source_devices(struct list_head *head)
+{
+ struct list_head *tail = head->next;
+ struct aer_device *dev;
+
+ while (head != tail) {
+ dev = container_of(tail, struct aer_device, node);
+ if (PCIE_PORT(dev->attribute.type))
+ free_source_devices(&dev->children);
+ tail = tail->next;
+ list_del(&dev->node);
+ kfree(dev);
+ }
+}
+
+/**
+ * search_source_device - search for an aer device which has id match
+ * @rpc: pointer to a Root Port hierarchy
+ * @id: an aer device ID to be searched
+ *
+ * Invoked to find an aer device, in a Root Port hierarchy, with a match.
+ **/
+static struct aer_device* search_source_device(struct aer_rpc *rpc, u16 id)
+{
+ struct list_head *entry, *list = &rpc->children;
+ struct aer_device *parent, *device;
+
+ if (list_empty(list))
+ return NULL;
+
+ /* Acquire Root semaphore access before searching for a device */
+ down(&rpc->rpc_sema);
+ if ((parent = search_direct_parent(list, id)))
+ list = &parent->children;
+
+ list_for_each(entry, list) {
+ device = container_of(entry, struct aer_device, node);
+ if (device->requestor_id != id)
+ continue;
+
+ /* MATCH found */
+ up(&rpc->rpc_sema);
+ return device;
+ }
+ up(&rpc->rpc_sema);
+
+ return NULL;
+}
+
+/**
+ * reorganize_dependents - shuffle aer devices
+ * @parent: pointer to a parent aer device directly linked to a Switch.
+ * @sw: pointer to Switch aer device data structure
+ *
+ * Invoked when a PCIE Switch ARE driver registers its AER handle with
+ * AER Root driver. Reshuffle aer devices in a hierarchy associated with
+ * this Switch if applied. Caller must acquire Root semaphore "rpc_sema"
+ * prior to calling.
+ **/
+static void reorganize_dependents(struct aer_device *parent,
+ struct aer_device *sw)
+{
+ struct list_head *head = &parent->children;
+ struct list_head *tail = head->next;
+ struct aer_device *dev;
+ int bus;
+
+ while (head != tail) {
+ dev = container_of(tail, struct aer_device, node);
+ tail = tail->next;
+ bus = AER_DEVICE_BUS(dev->requestor_id);
+ if (bus >= sw->attribute.p2p.bus.secondary &&
+ bus <= sw->attribute.p2p.bus.subordinate) {
+ dev->parent = sw;
+ list_del(&dev->node);
+ list_add_tail(&dev->node, &sw->children);
+ }
+ }
+}
+
+/**
+ * aer_device_detach - remove AER device from its associated Root Port list
+ * @rpc: associated Root Port
+ * @device: AER device being removed
+ *
+ * Invoked when AER endpoint driver unregisters its AER handle.
+ **/
+static void aer_device_detach(struct aer_rpc *rpc, struct aer_device *device)
+{
+ struct list_head *head, *tail;
+
+ head = &device->children;
+ tail = head->next;
+
+ /* Acquire Root semaphore access before detaching a device */
+ down(&rpc->rpc_sema);
+ while (head != tail) {
+ struct aer_device *tmp;
+
+ tmp = container_of(tail, struct aer_device, node);
+ tail = tail->next;
+ list_del(&tmp->node);
+ tmp->parent = device->parent;
+ list_add_tail(&tmp->node, &device->parent->children);
+ }
+ list_del(&device->node);
+ up(&rpc->rpc_sema);
+ kfree(device);
+}
+
+/**
+ * add_child_to_rootport - add AER device into its associated Root Port list
+ * @rpc: associated Root Port
+ * @child: AER device being added
+ *
+ * Invoked when AER endpoint driver registers its AER handle.
+ **/
+static void add_child_to_rootport(struct aer_rpc *rpc, struct aer_device *child)
+{
+ struct list_head *head = &rpc->children;
+ struct aer_device *parent;
+
+ /* Acquire Root sema access before adding a device into RPC hierarchy */
+ down(&rpc->rpc_sema);
+ if (PCIE_ROOT_PORT(child->attribute.type))
+ list_add_tail(&child->node, head);
+ else {
+ if (!(parent = search_direct_parent(head, child->requestor_id)))
+ parent = container_of(head->next,struct aer_device,node);
+ if (PCIE_SWITCH_PORT(child->attribute.type))
+ reorganize_dependents(parent, child);
+ child->parent = parent;
+ list_add_tail(&child->node, &parent->children);
+ }
+ up(&rpc->rpc_sema);
+}
+
+/**
+ * prepare_recovery_link - prepare downstream drivers for link recovery
+ * @head: pointer to a children list of a Switch port device
+ *
+ * Invoked when a Switch port device, which supports a hot link reset,
+ * detects a fatal error. Notify all downstream drivers under this
+ * Switch port device to prepare for a link recovery.
+ *
+ * Caller must acquire Root semaphore "rpc_sema" prior to calling.
+ **/
+static void prepare_recovery_link(struct list_head *head)
+{
+ struct list_head *entry;
+ struct aer_device *dev;
+
+ list_for_each(entry, head) {
+ dev = container_of(entry, struct aer_device, node);
+ if (PCIE_PORT(dev->attribute.type))
+ prepare_recovery_link(&dev->children);
+ if (dev->handle)
+ dev->handle->link_rec_prepare(dev->requestor_id);
+ }
+}
+
+/**
+ * restart_recovery_link - restart downstream drivers after link recovery
+ * @head: pointer to a children list of a Switch port device
+ *
+ * Invoked after a switch port driver completes a link recovery. Notify
+ * downstream drivers under this link to restart their activities.
+ *
+ * Caller must acquire Root semaphore "rpc_sema" prior to calling.
+ **/
+static void restart_recovery_link(struct list_head *head)
+{
+ struct list_head *entry;
+ struct aer_device *dev;
+
+ list_for_each(entry, head) {
+ dev = container_of(entry, struct aer_device, node);
+ if (PCIE_PORT(dev->attribute.type))
+ restart_recovery_link(&dev->children);
+ if (dev->handle)
+ dev->handle->link_rec_restart(dev->requestor_id);
+ }
+}
+
+/**
+ * do_link_reset - request a Switch driver to perform a hot link reset.
+ * @device: pointer to a Switch aer device data structure
+ *
+ * Invoked when a Switch device, which supports a hot link reset, detects
+ * a fatal error (fatal error recovery).
+ **/
+static void do_link_reset(struct aer_device *device)
+{
+ struct aer_rpc *rpc = search_node(device->requestor_id);
+
+ /*
+ * We need to prepare all downstream drivers associated in this
+ * Switch port. Acquire Root Port semaphore is required.
+ */
+ down(&rpc->rpc_sema);
+ /* Notify downstream drivers for preparing link reset */
+ prepare_recovery_link(&device->children);
+
+ /* Call Switch Port driver to do link reset */
+ device->handle->link_reset(device->requestor_id);
+
+ /* Notify downstream drivers for restart their activities */
+ restart_recovery_link(&device->children);
+ up(&rpc->rpc_sema);
+}
+
+/**
+ * alloc_aer_device - allocate an aer device data structure
+ * @requestor_id: a requestor ID
+ *
+ * Invoked when an AER service driver registers its AER handle with
+ * PCIE AER service driver.
+ **/
+static struct aer_device* alloc_aer_device(unsigned short requestor_id)
+{
+ struct aer_device *device;
+
+ if (!(device = (struct aer_device *)kmalloc(sizeof(struct aer_device),
+ GFP_KERNEL)))
+ return NULL;
+
+ memset(device, 0, sizeof(struct aer_device));
+ device->parent = NULL;
+ INIT_LIST_HEAD(&device->node);
+ INIT_LIST_HEAD(&device->children);
+ device->vendor = device->device = device->class_code = 0;
+ device->requestor_id = requestor_id;
+ device->attribute.type = 0;
+ device->handle = NULL;
+ device->correctables = device->nonfatals = device->fatals = 0;
+ sema_init(&device->d_sema, 1);
+
+ return device;
+}
+
+/**
+ * handle_error_source - handle logging error into an event log
+ * @device: pointer to AER device data structure
+ * @type: an error type
+ * @multi: whether an error is first or multiple
+ *
+ * Invoked when an error being detected by Root Port.
+ **/
+static void handle_error_source(struct aer_device *device, int type, int multi)
+{
+ struct rtc_time rtc;
+ union aer_error error;
+
+ /*
+ * We must acquires a semaphore access into this device until
+ * an error information associated with this device is stored
+ * into an event log queue.
+ */
+ down(&device->d_sema);
+
+ /* Process error */
+ *(unsigned short*)&device->flags = 0;
+ error.type = type;
+ error.source.status = 0;
+ if (device->handle) {
+ /* Notify AER service driver of an error detected on its hw */
+ if (device->handle->notify(device->requestor_id, &error)) {
+ if (device->attribute.type != PCIE_RC_PORT)
+ printk(KERN_DEBUG "AER glitch on device[%04x]\n",
+ device->requestor_id);
+
+ /* Release device semaphore */
+ up(&device->d_sema);
+ return;
+ }
+ if (error.type != AER_CORRECTABLE) {
+ if (!device->handle->get_header(device->requestor_id,
+ &error, &device->tlp))
+ device->flags.tlp = 1;
+ if (error.type == AER_NONFATAL)
+ device->nonfatals++;
+ else if (error.type == AER_FATAL) {
+ device->fatals++;
+ if (PCIE_PORT(device->attribute.type) &&
+ device->handle->link_reset)
+ device->flags.reset = 1;
+ }
+ }
+ }
+ if (error.type == AER_CORRECTABLE)
+ device->correctables++;
+ else
+ device->uncorrectables++;
+ device->flags.multi = (multi) ? 1 : 0;
+ device->last_recorded_err.type = error.type;
+ device->last_recorded_err.source.status = error.source.status;
+
+ /* Store time stamp */
+ rtc_get_rtc_time(&rtc);
+ device->time_stamp.seconds = rtc.tm_sec;
+ device->time_stamp.minutes = rtc.tm_min;
+ device->time_stamp.hours = rtc.tm_hour;
+ device->time_stamp.day = rtc.tm_mday;
+ device->time_stamp.month = rtc.tm_mon + 1;
+ device->time_stamp.year = rtc.tm_year;
+ device->time_stamp.century = 20;
+
+ /* Record an error event log in raw format */
+ aer_log_event(device);
+ aer_send_alert(&error);
+
+ /* Do link recovery */
+ if (device->flags.reset)
+ do_link_reset(device);
+
+ /* Release device semaphore */
+ up(&device->d_sema);
+}
+
+/**
+ * enable_root_aer - enable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus loads AER service driver.
+ **/
+static void enable_root_aer(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ int pos;
+ u16 reg16;
+ u32 reg32;
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ /* Clear PCIE Capability's Device Status */
+ pci_read_config_word(pdev, pos+PCIE_CAP_DEVICE_STATUS_REG_OFF, &reg16);
+ pci_write_config_word(pdev, pos+PCIE_CAP_DEVICE_STATUS_REG_OFF, reg16);
+
+ /* Disable system error generation in response to error messages */
+ pci_read_config_word(pdev, pos + PCIE_CAP_ROOT_CONTROL_REG_OFF, &reg16);
+ reg16 &= SYSTEM_ERROR_INTR_ON_MESG_DISABLED;
+ pci_write_config_word(pdev, pos + PCIE_CAP_ROOT_CONTROL_REG_OFF, reg16);
+
+ /* Clear error status */
+ pci_read_config_dword(pdev, ROOT_ERROR_STATUS_REG, &reg32);
+ pci_write_config_dword(pdev, ROOT_ERROR_STATUS_REG, reg32);
+
+ /* Init Correctable Masks */
+ pci_read_config_dword(pdev, CORRECTABLE_ERROR_STATUS_REG, &reg32);
+ pci_write_config_dword(pdev, CORRECTABLE_ERROR_STATUS_REG, reg32);
+ reg32 = ROOT_CONFIG_COR_ERROR_MASKS;
+ pci_write_config_dword(pdev, CORRECTABLE_ERROR_MASK_REG, reg32);
+
+ /* Init Uncorrectable Severity */
+ pci_read_config_dword(pdev, UNCORRECTABLE_ERROR_STATUS_REG, &reg32);
+ pci_write_config_dword(pdev, UNCORRECTABLE_ERROR_STATUS_REG, reg32);
+ reg32 = ROOT_CONFIG_UNCOR_ERROR_MASKS;
+ pci_write_config_dword(pdev, UNCORRECTABLE_ERROR_MASK_REG, reg32);
+ reg32 = ROOT_CONFIG_UNCOR_ERROR_SEVERITY;
+ pci_write_config_dword(pdev, UNCORRECTABLE_ERROR_SEVERITY_REG, reg32);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_write_config_dword(pdev, ROOT_ERROR_COMMAND_REG,
+ ROOT_PORT_INTR_ON_MESG_ENABLED);
+
+ /* Enable Root Port device reporting error itself */
+ pci_read_config_word(pdev, pos+PCIE_CAP_DEVICE_CONTROL_REG_OFF, &reg16);
+ pci_write_config_word(pdev, pos+PCIE_CAP_DEVICE_CONTROL_REG_OFF,
+ reg16 | ROOT_ERR_REPORT_ENABLE_BITS);
+}
+
+/**
+ * disable_root_aer - disable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus unloads AER service driver.
+ **/
+static void disable_root_aer(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ u32 reg32;
+
+ /* Disable Root's interrupt in response to error messages */
+ pci_read_config_dword(pdev, ROOT_ERROR_STATUS_REG, &reg32);
+ pci_write_config_dword(pdev, ROOT_ERROR_STATUS_REG, reg32);
+ pci_write_config_dword(pdev, ROOT_ERROR_COMMAND_REG, 0);
+}
+
+/**
+ * aer_osc_setup - run ACPI _OSC method
+ *
+ * Return:
+ * Zero if success. Nonzero for otherwise.
+ *
+ * Invoked when PCIE bus loads AER service driver. To avoid conflict with
+ * BIOS AER support requires BIOS to yield AER control to OS native driver.
+ **/
+static int aer_osc_setup(void)
+{
+ int retval = OSC_METHOD_RUN_SUCCESS;
+ acpi_status status;
+
+ pci_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT);
+ status = pci_osc_control_set(OSC_PCI_EXPRESS_AER_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_SUPPORT)
+ retval = OSC_METHOD_NOT_SUPPORTED;
+ else
+ retval = OSC_METHOD_RUN_FAILURE;
+ }
+
+ return retval;
+}
+
+/**
+ * aer_device_attach - attach AER device into its associated Root Port list
+ * @rpc: associated Root Port
+ * @device: AER device being attached
+ *
+ * Invoked when AER endpoint driver registers its callbacks.
+ *
+ * Return:
+ * Zero if success. -EINVAL if already registered.
+ **/
+static int aer_device_attach(struct aer_rpc *rpc, struct aer_device *device)
+{
+ struct aer_device *tmp;
+ int status = AER_SUCCESS;
+
+ if ((tmp = search_source_device(rpc, device->requestor_id))) {
+ if (!tmp->handle) {
+ /*
+ * Exist but not registered. Update contents of a new
+ * from old copy. Then remove old copy.
+ */
+ device->correctables = tmp->correctables;
+ device->last_recorded_err = tmp->last_recorded_err;
+ memcpy(&device->time_stamp, &tmp->time_stamp,
+ sizeof(struct aer_record_time_stamp));
+ down(&rpc->rpc_sema); /* Acquire Root semaphore */
+ list_del(&tmp->node);
+ up(&rpc->rpc_sema);
+ kfree(tmp);
+ } else /* Already has a registered handle */
+ status = -EINVAL;
+ }
+ if (!status)
+ add_child_to_rootport(rpc, device);
+
+ return status;
+}
+
+/**
+ * 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.
+ **/
+static struct err_source* get_e_source(struct aer_rpc *rpc)
+{
+ struct 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;
+}
+
+struct pci_dev* get_root_pci_dev(unsigned short requestor_id)
+{
+ struct aer_rpc *rpc;
+
+ rpc = search_node(requestor_id);
+
+ return (rpc) ? rpc->rpd->port : NULL;
+}
+
+/**
+ * aer_isr - consume an error detected by root port
+ * @context: pointer to a private data of pcie device
+ *
+ * Invoked, as DPC, when root port records new detected error
+ **/
+void aer_isr(void *context)
+{
+ struct pcie_device *pdev = (struct pcie_device *)context;
+ struct aer_rpc *rpc = get_service_data(pdev);
+ struct aer_device *device;
+ struct err_source *e_source;
+
+ if (!(e_source = get_e_source(rpc))) {
+ printk(KERN_DEBUG "%s->DPC fails to get an error source\n",
+ __FUNCTION__);
+ return;
+ }
+
+ if (ROOT_ERR_STATUS_CORRECTABLE(e_source->status)) {
+ if (!(device = search_source_device(rpc,
+ ERR_COR_ID(e_source->id)))) {
+ /* Detect agent's unregistered driver */
+ if (!(device = alloc_aer_device(
+ ERR_COR_ID(e_source->id))))
+ return;
+
+ add_child_to_rootport(rpc, device);
+ }
+ handle_error_source(device, AER_CORRECTABLE,
+ e_source->status & 0x2);
+ }
+ if (ROOT_ERR_STATUS_UNCORRECTABLE(e_source->status)) {
+ if (!(device = search_source_device(rpc,
+ ERR_UNCOR_ID(e_source->id)))) {
+ /* Detect agent's unregistered driver */
+ if (!(device = alloc_aer_device(
+ ERR_UNCOR_ID(e_source->id))))
+ return;
+
+ add_child_to_rootport(rpc, device);
+ }
+ handle_error_source(device, AER_UNCORRECTABLE,
+ e_source->status & 0x8);
+ }
+}
+
+/**
+ * aer_add_rootport - add a new root port into Root Complex's port hierarchy
+ * @rpc: pointer to a new root port device being added
+ *
+ * Invoked when AER service loaded on a new Root Port
+ **/
+int aer_add_rootport(struct aer_rpc *rpc)
+{
+ list_add_tail(&rpc->node, &rc_list);
+
+ /* Enable root port AER itself */
+ enable_root_aer(rpc);
+
+ /* Self registering */
+ return pcie_aer_register(rpc->rpd->port, &aer_root_handle);
+}
+
+/**
+ * aer_delete_rootport - delete a root port from Root Complex's port hierarchy
+ * @rpc: pointer to a root port device being deleted
+ *
+ * Invoked when AER service unloaded on a specific Root Port
+ **/
+void aer_delete_rootport(struct aer_rpc *rpc)
+{
+ struct list_head *head = &rpc->children;
+
+ /* Disable root port AER itself */
+ disable_root_aer(rpc);
+
+ /* Acquire Root semaphore access before removing source devices */
+ down(&rpc->rpc_sema);
+
+ /* Free all source nodes under this root port */
+ free_source_devices(head);
+ list_del(&rpc->node);
+ up(&rpc->rpc_sema);
+ kfree(rpc);
+}
+
+/**
+ * aer_fsprint_devices - display all registered devices
+ * @page: pointer to a buffer holding readable display format
+ *
+ * Invoked by user interface status
+ **/
+int aer_fsprint_devices(char *page)
+{
+ struct list_head *entry;
+ struct aer_rpc *rpc;
+ char *p = page;
+
+ if (!list_empty(&rc_list)) {
+ list_for_each(entry, &rc_list) {
+ rpc = container_of(entry, struct aer_rpc, node);
+ p += sprintf(p, "Root Port [%d.%d.%d] Device List:\n",
+ rpc->primary, rpc->secondary, rpc->subordinate);
+ down(&rpc->rpc_sema); /* Must acquire Root sema */
+ p += print_source_devices(&rpc->children, p);
+ up(&rpc->rpc_sema);
+ }
+ }
+
+ return p-page;
+}
+
+/**
+ * aer_set_verbose - set a verbose mode
+ *
+ * Invoked by user interface verbose
+ **/
+void aer_set_verbose(int value)
+{
+ if (value & VERBOSE_MASK)
+ verbose = value;
+}
+
+/**
+ * aer_get_verbose - return a verbose mode
+ *
+ * Invoked by user interface verbose
+ **/
+int aer_get_verbose(void) {return verbose;}
+
+/**
+ * aer_fsprint_record - consume an error record
+ * @page: pointer to a buffer holding readable display format
+ *
+ * Invoked by user interface consume
+ **/
+int aer_fsprint_record(char *page)
+{
+ char *p = page;
+
+ p += aer_get_record(p, verbose);
+
+ return p - page;
+}
+
+/**
+ * aer_set_auto_mode - set an auto consumption mode
+ * @mode: zero for manual or nonzero automatic
+ *
+ * Invoked by user interface auto
+ **/
+void aer_set_auto_mode(int mode)
+{
+ if ((auto_mode = mode))
+ aer_set_auto_consume();
+}
+
+/**
+ * aer_get_auto_mode - return an auto consumption mode
+ *
+ * Invoked by user interface auto
+ **/
+int aer_get_auto_mode(void) {return auto_mode;}
+
+/**
+ * aer_init - provide AER initialization
+ * @drv: pointer to AER service driver
+ *
+ * Invoked when AER service driver is loaded.
+ **/
+int aer_init(struct pcie_port_service_driver *drv)
+{
+ int status;
+
+ if (aer_init_flag)
+ return AER_SUCCESS;
+
+ /* Run _OSC Method */
+ if (!(status = aer_osc_setup())) {
+ /* Init AER */
+ if (!(status = aer_sysfs_init(&drv->driver))) {
+ if ((status = aer_event_log_init()))
+ aer_sysfs_cleanup(&drv->driver);
+ }
+ }
+ if (!status)
+ aer_init_flag = 1; /* Set to indicate AER Init DONE */
+
+ return status;
+}
+
+/**
+ * aer_cleanup - provide a cleanup
+ * @drv: pointer to AER service driver
+ *
+ * Invoked when AER service driver is unloaded from Root Ports.
+ * Cleanup only when rc_list is empty.
+ **/
+void aer_cleanup(struct pcie_port_service_driver *drv)
+{
+ if (!aer_init_flag || !list_empty(&rc_list))
+ return;
+
+ aer_sysfs_cleanup(&drv->driver);
+ aer_event_log_cleanup();
+ aer_init_flag = 0; /* Reset this flag */
+}
+
+/**
+ * pcie_aer_register - provide Root API for endpoint to register its AER
+ * @dev: pointer to the pci_dev data structure
+ * @handle: pointer to the pcie_aer_handle data structure
+ *
+ * Invoked when AER endpoint driver registers its callbacks to Root AER driver.
+ **/
+int pcie_aer_register(struct pci_dev *dev, struct pcie_aer_handle *handle)
+{
+ struct aer_device *device = NULL;
+ struct aer_rpc *rpc = NULL;
+ int pos, status;
+ u32 bus;
+ u16 type, requestor_id;
+
+ if (!(dev && handle) || !(pos = pci_find_capability(dev,PCI_CAP_ID_EXP)))
+ return -EINVAL; /* Invalid request */
+
+ requestor_id = (dev->bus->number << 8) | dev->devfn;
+ if (!(rpc = search_node(requestor_id)))
+ return -EINVAL; /* No Root Port associated */
+
+ if (!(device = alloc_aer_device(requestor_id)))
+ return -ENOMEM;
+
+ /* Initialize AER device corresponding to a requested handle */
+ device->vendor = dev->vendor;
+ device->device = dev->device;
+ device->class_code = dev->class;
+ device->handle = handle;
+ pci_read_config_word(dev, pos + 2, &type);
+ device->attribute.type = (type >> 4) & PCIE_PORT_TYPE_MASK;
+ if (PCIE_PORT(device->attribute.type)) {
+ pci_read_config_dword(dev, PCI_PRIMARY_BUS, &bus);
+ device->attribute.p2p.bus.primary = bus & 0xff;
+ device->attribute.p2p.bus.secondary = (bus >> 8) & 0xff;
+ device->attribute.p2p.bus.subordinate = (bus >> 16) & 0xff;
+ }
+ if ((status = aer_device_attach(rpc, device))) {
+ printk(KERN_DEBUG "Req. AER handle is already exist. Ignore!\n");
+ kfree(device);
+ }
+
+ return status;
+}
+
+/**
+ * pcie_aer_unregister - provide Root API for endpoint to unregister its AER
+ * @requestor_id: endpoint source ID
+ *
+ * Invoked when AER endpoint driver unregisters its callbacks.
+ **/
+void pcie_aer_unregister(unsigned short requestor_id)
+{
+ struct aer_rpc *rpc;
+ struct aer_device *device;
+
+ if ((rpc = search_node(requestor_id))) {
+ if ((device = search_source_device(rpc, requestor_id)))
+ aer_device_detach(rpc, device);
+ }
+}
+
+EXPORT_SYMBOL(pcie_aer_register);
+EXPORT_SYMBOL(pcie_aer_unregister);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/