# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1227524694 0
# Node ID 412b24a36929b7cbedc793b4aad06b334bea021b
# Parent 5888ffa4b252f11749b3fde82eeb5ab68bb2e537
pciback: error handler for PCIE_AER.
This patch is the main implementation for enabling PCIE_AER handling,
adding related pci error handler in pciback and pcifront.
When a device sends a PCIE error message to the root port, it will
trigger an interrupt. The irq handler will then collect roor error
status register, then schedule a work to process the error based on
the error type.
If the error is non-correctable error (fatal or non-fatal), AER
service driver will call the callback funtions of the endpoint's
driver. For bridge, it will broadcast the error to the downstream
ports. Pciback error handler will be called accordingly. Pciback then
ask pcifront help to call the end-device driver for finally completing
the related pci error handling jobs.
Signed-off-by: Jiang Yunhong <yunhong.jiang@xxxxxxxxx>
Signed-off-by: Ke Liping <liping.ke@xxxxxxxxx>
---
drivers/xen/pciback/controller.c | 35 ++++
drivers/xen/pciback/passthrough.c | 10 +
drivers/xen/pciback/pci_stub.c | 319 ++++++++++++++++++++++++++++++++++++++
drivers/xen/pciback/pciback.h | 15 +
drivers/xen/pciback/pciback_ops.c | 25 ++
drivers/xen/pciback/slot.c | 30 +++
drivers/xen/pciback/vpci.c | 30 +++
drivers/xen/pciback/xenbus.c | 9 -
drivers/xen/pcifront/pci_op.c | 115 +++++++++++++
drivers/xen/pcifront/pcifront.h | 13 +
drivers/xen/pcifront/xenbus.c | 14 +
include/xen/interface/io/pciif.h | 35 +++-
12 files changed, 639 insertions(+), 11 deletions(-)
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/controller.c
--- a/drivers/xen/pciback/controller.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/controller.c Mon Nov 24 11:04:54 2008 +0000
@@ -406,3 +406,38 @@ void pciback_release_devices(struct pcib
kfree(dev_data);
pdev->pci_dev_data = NULL;
}
+
+int pciback_get_pcifront_dev(struct pci_dev *pcidev,
+ struct pciback_device *pdev,
+ unsigned int *domain, unsigned int *bus, unsigned int *devfn)
+{
+ struct controller_dev_data *dev_data = pdev->pci_dev_data;
+ struct controller_dev_entry *dev_entry;
+ struct controller_list_entry *cntrl_entry;
+ unsigned long flags;
+ int found = 0;
+ spin_lock_irqsave(&dev_data->lock, flags);
+
+ list_for_each_entry(cntrl_entry, &dev_data->list, list) {
+ list_for_each_entry(dev_entry, &cntrl_entry->dev_list, list) {
+ if ( (dev_entry->dev->bus->number ==
+ pcidev->bus->number) &&
+ (dev_entry->dev->devfn ==
+ pcidev->devfn) &&
+ (pci_domain_nr(dev_entry->dev->bus) ==
+ pci_domain_nr(pcidev->bus)))
+ {
+ found = 1;
+ *domain = cntrl_entry->domain;
+ *bus = cntrl_entry->bus;
+ *devfn = dev_entry->devfn;
+ goto out;
+ }
+ }
+ }
+out:
+ spin_unlock_irqrestore(&dev_data->lock, flags);
+ return found;
+
+}
+
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/passthrough.c
--- a/drivers/xen/pciback/passthrough.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/passthrough.c Mon Nov 24 11:04:54 2008 +0000
@@ -164,3 +164,13 @@ void pciback_release_devices(struct pcib
kfree(dev_data);
pdev->pci_dev_data = NULL;
}
+
+int pciback_get_pcifront_dev(struct pci_dev *pcidev, struct pciback_device
*pdev,
+ unsigned int *domain, unsigned int *bus, unsigned int *devfn)
+
+{
+ *domain = pci_domain_nr(pcidev->bus);
+ *bus = pcidev->bus->number;
+ *devfn = pcidev->devfn;
+ return 1;
+}
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/pci_stub.c
--- a/drivers/xen/pciback/pci_stub.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/pci_stub.c Mon Nov 24 11:04:54 2008 +0000
@@ -6,15 +6,24 @@
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/rwsem.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/kref.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
#include <asm/atomic.h>
+#include <xen/evtchn.h>
#include "pciback.h"
#include "conf_space.h"
#include "conf_space_quirks.h"
static char *pci_devs_to_hide = NULL;
+wait_queue_head_t aer_wait_queue;
+/*Add sem for sync AER handling and pciback remove/reconfigue ops,
+* We want to avoid in middle of AER ops, pciback devices is being removed
+*/
+static DECLARE_RWSEM(pcistub_sem);
module_param_named(hide, pci_devs_to_hide, charp, 0444);
struct pcistub_device_id {
@@ -207,6 +216,10 @@ void pcistub_put_pci_dev(struct pci_dev
spin_unlock_irqrestore(&pcistub_devices_lock, flags);
+ /*hold this lock for avoiding breaking link between
+ * pcistub and pciback when AER is in processing
+ */
+ down_write(&pcistub_sem);
/* Cleanup our device
* (so it's ready for the next domain)
*/
@@ -219,6 +232,7 @@ void pcistub_put_pci_dev(struct pci_dev
spin_unlock_irqrestore(&found_psdev->lock, flags);
pcistub_device_put(found_psdev);
+ up_write(&pcistub_sem);
}
static int __devinit pcistub_match_one(struct pci_dev *dev,
@@ -279,6 +293,8 @@ static int __devinit pcistub_init_device
pci_set_drvdata(dev, dev_data);
dev_dbg(&dev->dev, "initializing config\n");
+
+ init_waitqueue_head(&aer_wait_queue);
err = pciback_config_init_dev(dev);
if (err)
goto out;
@@ -477,6 +493,308 @@ static const struct pci_device_id pcistu
{0,},
};
+static void kill_domain_by_device(struct pcistub_device *psdev)
+{
+ struct xenbus_transaction xbt;
+ int err;
+ char nodename[1024];
+
+ if (!psdev)
+ dev_err(&psdev->dev->dev,
+ "device is NULL when do AER recovery/kill_domain\n");
+ sprintf(nodename, "/local/domain/0/backend/pci/%d/0",
+ psdev->pdev->xdev->otherend_id);
+ nodename[strlen(nodename)] = '\0';
+
+again:
+ err = xenbus_transaction_start(&xbt);
+ if (err)
+ {
+ dev_err(&psdev->dev->dev,
+ "error %d when start xenbus transaction\n", err);
+ return;
+ }
+ /*PV AER handlers will set this flag*/
+ xenbus_printf(xbt, nodename, "aerState" , "aerfail" );
+ err = xenbus_transaction_end(xbt, 0);
+ if (err)
+ {
+ if (err == -EAGAIN)
+ goto again;
+ dev_err(&psdev->dev->dev,
+ "error %d when end xenbus transaction\n", err);
+ return;
+ }
+}
+
+/* For each aer recovery step error_detected, mmio_enabled, etc, front_end and
+ * backend need to have cooperation. In pciback, those steps will do similar
+ * jobs: send service request and waiting for front_end response.
+*/
+static pci_ers_result_t common_process(struct pcistub_device *psdev,
+ pci_channel_state_t state, int aer_cmd, pci_ers_result_t result)
+{
+ pci_ers_result_t res = result;
+ struct xen_pcie_aer_op *aer_op;
+ int ret;
+
+ /*with PV AER drivers*/
+ aer_op = &(psdev->pdev->sh_info->aer_op);
+ aer_op->cmd = aer_cmd ;
+ /*useful for error_detected callback*/
+ aer_op->err = state;
+ /*pcifront_end BDF*/
+ ret = pciback_get_pcifront_dev(psdev->dev, psdev->pdev,
+ &aer_op->domain, &aer_op->bus, &aer_op->devfn);
+ if (!ret) {
+ dev_err(&psdev->dev->dev,
+ "pciback: failed to get pcifront device\n");
+ return PCI_ERS_RESULT_NONE;
+ }
+ wmb();
+
+ dev_dbg(&psdev->dev->dev,
+ "pciback: aer_op %x dom %x bus %x devfn %x\n",
+ aer_cmd, aer_op->domain, aer_op->bus, aer_op->devfn);
+ /*local flag to mark there's aer request, pciback callback will use this
+ * flag to judge whether we need to check pci-front give aer service
+ * ack signal
+ */
+ set_bit(_PCIB_op_pending, (unsigned long *)&psdev->pdev->flags);
+
+ /*It is possible that a pcifront conf_read_write ops request invokes
+ * the callback which cause the spurious execution of wake_up.
+ * Yet it is harmless and better than a spinlock here
+ */
+ set_bit(_XEN_PCIB_active,
+ (unsigned long *)&psdev->pdev->sh_info->flags);
+ wmb();
+ notify_remote_via_irq(psdev->pdev->evtchn_irq);
+
+ ret = wait_event_timeout(aer_wait_queue, !(test_bit(_XEN_PCIB_active,
+ (unsigned long *)&psdev->pdev->sh_info->flags)), 300*HZ);
+
+ if (!ret) {
+ if (test_bit(_XEN_PCIB_active,
+ (unsigned long *)&psdev->pdev->sh_info->flags)) {
+ dev_err(&psdev->dev->dev,
+ "pcifront aer process not responding!\n");
+ clear_bit(_XEN_PCIB_active,
+ (unsigned long *)&psdev->pdev->sh_info->flags);
+ aer_op->err = PCI_ERS_RESULT_NONE;
+ return res;
+ }
+ }
+ clear_bit(_PCIB_op_pending, (unsigned long *)&psdev->pdev->flags);
+
+ if ( test_bit( _XEN_PCIF_active,
+ (unsigned long*)&psdev->pdev->sh_info->flags)) {
+ dev_dbg(&psdev->dev->dev,
+ "schedule pci_conf service in pciback \n");
+ test_and_schedule_op(psdev->pdev);
+ }
+
+ res = (pci_ers_result_t)aer_op->err;
+ return res;
+}
+
+/*
+* pciback_slot_reset: it will send the slot_reset request to pcifront in case
+* of the device driver could provide this service, and then wait for pcifront
+* ack.
+* @dev: pointer to PCI devices
+* return value is used by aer_core do_recovery policy
+*/
+static pci_ers_result_t pciback_slot_reset(struct pci_dev *dev)
+{
+ struct pcistub_device *psdev;
+ pci_ers_result_t result;
+
+ result = PCI_ERS_RESULT_RECOVERED;
+ dev_dbg(&dev->dev, "pciback_slot_reset(bus:%x,devfn:%x)\n",
+ dev->bus->number, dev->devfn);
+
+ down_write(&pcistub_sem);
+ psdev = pcistub_device_find(pci_domain_nr(dev->bus),
+ dev->bus->number,
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ if ( !psdev || !psdev->pdev || !psdev->pdev->sh_info )
+ {
+ dev_err(&dev->dev,
+ "pciback device is not found/in use/connected!\n");
+ goto end;
+ }
+ if ( !test_bit(_XEN_PCIB_AERHANDLER,
+ (unsigned long *)&psdev->pdev->sh_info->flags) ) {
+ dev_err(&dev->dev,
+ "guest with no AER driver should have been killed\n");
+ goto release;
+ }
+ result = common_process(psdev, 1, XEN_PCI_OP_aer_slotreset, result);
+
+ if (result == PCI_ERS_RESULT_NONE ||
+ result == PCI_ERS_RESULT_DISCONNECT) {
+ dev_dbg(&dev->dev,
+ "No AER slot_reset service or disconnected!\n");
+ kill_domain_by_device(psdev);
+ }
+release:
+ pcistub_device_put(psdev);
+end:
+ up_write(&pcistub_sem);
+ return result;
+
+}
+
+
+/*pciback_mmio_enabled: it will send the mmio_enabled request to pcifront
+* in case of the device driver could provide this service, and then wait
+* for pcifront ack.
+* @dev: pointer to PCI devices
+* return value is used by aer_core do_recovery policy
+*/
+
+static pci_ers_result_t pciback_mmio_enabled(struct pci_dev *dev)
+{
+ struct pcistub_device *psdev;
+ pci_ers_result_t result;
+
+ result = PCI_ERS_RESULT_RECOVERED;
+ dev_dbg(&dev->dev, "pciback_mmio_enabled(bus:%x,devfn:%x)\n",
+ dev->bus->number, dev->devfn);
+
+ down_write(&pcistub_sem);
+ psdev = pcistub_device_find(pci_domain_nr(dev->bus),
+ dev->bus->number,
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ if ( !psdev || !psdev->pdev || !psdev->pdev->sh_info)
+ {
+ dev_err(&dev->dev,
+ "pciback device is not found/in use/connected!\n");
+ goto end;
+ }
+ if ( !test_bit(_XEN_PCIB_AERHANDLER,
+ (unsigned long *)&psdev->pdev->sh_info->flags) ) {
+ dev_err(&dev->dev,
+ "guest with no AER driver should have been killed\n");
+ goto release;
+ }
+ result = common_process(psdev, 1, XEN_PCI_OP_aer_mmio, result);
+
+ if (result == PCI_ERS_RESULT_NONE ||
+ result == PCI_ERS_RESULT_DISCONNECT) {
+ dev_dbg(&dev->dev,
+ "No AER mmio_enabled service or disconnected!\n");
+ kill_domain_by_device(psdev);
+ }
+release:
+ pcistub_device_put(psdev);
+end:
+ up_write(&pcistub_sem);
+ return result;
+}
+
+/*pciback_error_detected: it will send the error_detected request to pcifront
+* in case of the device driver could provide this service, and then wait
+* for pcifront ack.
+* @dev: pointer to PCI devices
+* @error: the current PCI connection state
+* return value is used by aer_core do_recovery policy
+*/
+
+static pci_ers_result_t pciback_error_detected(struct pci_dev *dev,
+ pci_channel_state_t error)
+{
+ struct pcistub_device *psdev;
+ pci_ers_result_t result;
+
+ result = PCI_ERS_RESULT_CAN_RECOVER;
+ dev_dbg(&dev->dev, "pciback_error_detected(bus:%x,devfn:%x)\n",
+ dev->bus->number, dev->devfn);
+
+ down_write(&pcistub_sem);
+ psdev = pcistub_device_find(pci_domain_nr(dev->bus),
+ dev->bus->number,
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ if ( !psdev || !psdev->pdev || !psdev->pdev->sh_info)
+ {
+ dev_err(&dev->dev,
+ "pciback device is not found/in use/connected!\n");
+ goto end;
+ }
+ /*Guest owns the device yet no aer handler regiested, kill guest*/
+ if ( !test_bit(_XEN_PCIB_AERHANDLER,
+ (unsigned long *)&psdev->pdev->sh_info->flags) ) {
+ dev_dbg(&dev->dev, "guest may have no aer driver, kill it\n");
+ kill_domain_by_device(psdev);
+ goto release;
+ }
+ result = common_process(psdev, error, XEN_PCI_OP_aer_detected, result);
+
+ if (result == PCI_ERS_RESULT_NONE ||
+ result == PCI_ERS_RESULT_DISCONNECT) {
+ dev_dbg(&dev->dev,
+ "No AER error_detected service or disconnected!\n");
+ kill_domain_by_device(psdev);
+ }
+release:
+ pcistub_device_put(psdev);
+end:
+ up_write(&pcistub_sem);
+ return result;
+}
+
+/*pciback_error_resume: it will send the error_resume request to pcifront
+* in case of the device driver could provide this service, and then wait
+* for pcifront ack.
+* @dev: pointer to PCI devices
+*/
+
+static void pciback_error_resume(struct pci_dev *dev)
+{
+ struct pcistub_device *psdev;
+
+ dev_dbg(&dev->dev, "pciback_error_resume(bus:%x,devfn:%x)\n",
+ dev->bus->number, dev->devfn);
+
+ down_write(&pcistub_sem);
+ psdev = pcistub_device_find(pci_domain_nr(dev->bus),
+ dev->bus->number,
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ if ( !psdev || !psdev->pdev || !psdev->pdev->sh_info)
+ {
+ dev_err(&dev->dev,
+ "pciback device is not found/in use/connected!\n");
+ goto end;
+ }
+
+ if ( !test_bit(_XEN_PCIB_AERHANDLER,
+ (unsigned long *)&psdev->pdev->sh_info->flags) ) {
+ dev_err(&dev->dev,
+ "guest with no AER driver should have been killed\n");
+ kill_domain_by_device(psdev);
+ goto release;
+ }
+ common_process(psdev, 1, XEN_PCI_OP_aer_resume,
PCI_ERS_RESULT_RECOVERED);
+release:
+ pcistub_device_put(psdev);
+end:
+ up_write(&pcistub_sem);
+ return;
+}
+
+/*add pciback AER handling*/
+static struct pci_error_handlers pciback_error_handler = {
+ .error_detected = pciback_error_detected,
+ .mmio_enabled = pciback_mmio_enabled,
+ .slot_reset = pciback_slot_reset,
+ .resume = pciback_error_resume,
+};
+
/*
* Note: There is no MODULE_DEVICE_TABLE entry here because this isn't
* for a normal device. I don't want it to be loaded automatically.
@@ -487,6 +805,7 @@ static struct pci_driver pciback_pci_dri
.id_table = pcistub_ids,
.probe = pcistub_probe,
.remove = pcistub_remove,
+ .err_handler = &pciback_error_handler,
};
static inline int str_to_slot(const char *buf, int *domain, int *bus,
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/pciback.h
--- a/drivers/xen/pciback/pciback.h Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/pciback.h Mon Nov 24 11:04:54 2008 +0000
@@ -22,6 +22,8 @@ struct pci_dev_entry {
#define _PDEVF_op_active (0)
#define PDEVF_op_active (1<<(_PDEVF_op_active))
+#define _PCIB_op_pending (1)
+#define PCIB_op_pending (1<<(_PCIB_op_pending))
struct pciback_device {
void *pci_dev_data;
@@ -81,6 +83,16 @@ struct pci_dev *pciback_get_pci_dev(stru
struct pci_dev *pciback_get_pci_dev(struct pciback_device *pdev,
unsigned int domain, unsigned int bus,
unsigned int devfn);
+
+/**
+* Add for domain0 PCIE-AER handling. Get guest domain/bus/devfn in pciback
+* before sending aer request to pcifront, so that guest could identify
+* device, coopearte with pciback to finish aer recovery job if device driver
+* has the capability
+*/
+
+int pciback_get_pcifront_dev(struct pci_dev *pcidev, struct pciback_device
*pdev,
+ unsigned int *domain, unsigned int *bus,
unsigned int *devfn);
int pciback_init_devices(struct pciback_device *pdev);
int pciback_publish_pci_roots(struct pciback_device *pdev,
publish_pci_root_cb cb);
@@ -108,4 +120,7 @@ int pciback_disable_msix(struct pciback_
struct pci_dev *dev, struct xen_pci_op *op);
#endif
extern int verbose_request;
+
+void test_and_schedule_op(struct pciback_device *pdev);
#endif
+
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/pciback_ops.c
--- a/drivers/xen/pciback/pciback_ops.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/pciback_ops.c Mon Nov 24 11:04:54 2008 +0000
@@ -4,6 +4,7 @@
* Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx>
*/
#include <linux/module.h>
+#include <linux/wait.h>
#include <asm/bitops.h>
#include <xen/evtchn.h>
#include "pciback.h"
@@ -37,14 +38,29 @@ void pciback_reset_device(struct pci_dev
}
}
}
-
-static inline void test_and_schedule_op(struct pciback_device *pdev)
+extern wait_queue_head_t aer_wait_queue;
+extern struct workqueue_struct *pciback_wq;
+/*
+* Now the same evtchn is used for both pcifront conf_read_write request
+* as well as pcie aer front end ack. We use a new work_queue to schedule
+* pciback conf_read_write service for avoiding confict with aer_core
+* do_recovery job which also use the system default work_queue
+*/
+void test_and_schedule_op(struct pciback_device *pdev)
{
/* Check that frontend is requesting an operation and that we are not
* already processing a request */
if (test_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags)
&& !test_and_set_bit(_PDEVF_op_active, &pdev->flags))
- schedule_work(&pdev->op_work);
+ {
+ queue_work(pciback_wq, &pdev->op_work);
+ }
+ /*_XEN_PCIB_active should have been cleared by pcifront. And also make
+ sure pciback is waiting for ack by checking _PCIB_op_pending*/
+ if (!test_bit(_XEN_PCIB_active,(unsigned long *)&pdev->sh_info->flags)
+ &&test_bit(_PCIB_op_pending, &pdev->flags)) {
+ wake_up(&aer_wait_queue);
+ }
}
/* Performing the configuration space reads/writes must not be done in atomic
@@ -103,7 +119,8 @@ void pciback_do_op(void *data)
smp_mb__after_clear_bit(); /* /before/ final check for work */
/* Check to see if the driver domain tried to start another request in
- * between clearing _XEN_PCIF_active and clearing _PDEVF_op_active. */
+ * between clearing _XEN_PCIF_active and clearing _PDEVF_op_active.
+ */
test_and_schedule_op(pdev);
}
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/slot.c
--- a/drivers/xen/pciback/slot.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/slot.c Mon Nov 24 11:04:54 2008 +0000
@@ -155,3 +155,33 @@ void pciback_release_devices(struct pcib
kfree(slot_dev);
pdev->pci_dev_data = NULL;
}
+
+int pciback_get_pcifront_dev(struct pci_dev *pcidev, struct pciback_device
*pdev,
+ unsigned int *domain, unsigned int *bus, unsigned int *devfn)
+{
+ int slot, busnr;
+ struct slot_dev_data *slot_dev = pdev->pci_dev_data;
+ struct pci_dev *dev;
+ int found = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&slot_dev->lock, flags);
+
+ for (busnr = 0; busnr < PCI_BUS_NBR; bus++)
+ for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
+ dev = slot_dev->slots[busnr][slot];
+ if (dev && dev->bus->number == pcidev->bus->number
+ && dev->devfn == pcidev->devfn
+ && pci_domain_nr(dev->bus) ==
pci_domain_nr(pcidev->bus)) {
+ found = 1;
+ *domain = 0;
+ *bus = busnr;
+ *devfn = PCI_DEVFN(slot,0);
+ goto out;
+ }
+ }
+out:
+ spin_unlock_irqrestore(&slot_dev->lock, flags);
+ return found;
+
+}
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/vpci.c
--- a/drivers/xen/pciback/vpci.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/vpci.c Mon Nov 24 11:04:54 2008 +0000
@@ -210,3 +210,33 @@ void pciback_release_devices(struct pcib
kfree(vpci_dev);
pdev->pci_dev_data = NULL;
}
+
+int pciback_get_pcifront_dev(struct pci_dev *pcidev, struct pciback_device
*pdev,
+ unsigned int *domain, unsigned int *bus, unsigned int *devfn)
+{
+ struct pci_dev_entry *entry;
+ struct pci_dev *dev = NULL;
+ struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
+ unsigned long flags;
+ int found = 0, slot;
+
+ spin_lock_irqsave(&vpci_dev->lock, flags);
+ for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
+ list_for_each_entry(entry,
+ &vpci_dev->dev_list[slot],
+ list) {
+ dev = entry->dev;
+ if (dev && dev->bus->number == pcidev->bus->number
+ && pci_domain_nr(dev->bus) ==
pci_domain_nr(pcidev->bus)
+ && dev->devfn == pcidev->devfn)
+ {
+ found = 1;
+ *domain = 0;
+ *bus = 0;
+ *devfn = PCI_DEVFN(slot,
PCI_FUNC(pcidev->devfn));
+ }
+ }
+ }
+ spin_unlock_irqrestore(&vpci_dev->lock, flags);
+ return found;
+}
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pciback/xenbus.c
--- a/drivers/xen/pciback/xenbus.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pciback/xenbus.c Mon Nov 24 11:04:54 2008 +0000
@@ -12,6 +12,7 @@
#include "pciback.h"
#define INVALID_EVTCHN_IRQ (-1)
+struct workqueue_struct *pciback_wq;
static struct pciback_device *alloc_pdev(struct xenbus_device *xdev)
{
@@ -694,11 +695,17 @@ int __init pciback_xenbus_register(void)
{
if (!is_running_on_xen())
return -ENODEV;
-
+ pciback_wq = create_workqueue("pciback_workqueue");
+ if (!pciback_wq) {
+ printk(KERN_ERR "pciback_xenbus_register: create"
+ "pciback_workqueue failed\n");
+ return -EFAULT;
+ }
return xenbus_register_backend(&xenbus_pciback_driver);
}
void __exit pciback_xenbus_unregister(void)
{
+ destroy_workqueue(pciback_wq);
xenbus_unregister_driver(&xenbus_pciback_driver);
}
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pcifront/pci_op.c
--- a/drivers/xen/pcifront/pci_op.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pcifront/pci_op.c Mon Nov 24 11:04:54 2008 +0000
@@ -8,6 +8,7 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
+#include <asm/bitops.h>
#include <linux/time.h>
#include <xen/evtchn.h>
#include "pcifront.h"
@@ -151,6 +152,15 @@ static int errno_to_pcibios_err(int errn
return PCIBIOS_SET_FAILED;
}
return errno;
+}
+
+static inline void schedule_pcifront_aer_op(struct pcifront_device *pdev)
+{
+ if (test_bit(_XEN_PCIB_active, (unsigned long *)&pdev->sh_info->flags)
+ && !test_and_set_bit(_PDEVB_op_active, &pdev->flags)) {
+ dev_dbg(&pdev->xdev->dev, "schedule aer frontend job\n");
+ schedule_work(&pdev->op_work);
+ }
}
static int do_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op)
@@ -199,6 +209,18 @@ static int do_pci_op(struct pcifront_dev
}
}
+ /*
+ * We might lose backend service request since we
+ * reuse same evtchn with pci_conf backend response. So re-schedule
+ * aer pcifront service.
+ */
+ if (test_bit(_XEN_PCIB_active,
+ (unsigned long*)&pdev->sh_info->flags)) {
+ dev_err(&pdev->xdev->dev,
+ "schedule aer pcifront service\n");
+ schedule_pcifront_aer_op(pdev);
+ }
+
memcpy(op, active_op, sizeof(struct xen_pci_op));
err = op->err;
@@ -549,3 +571,96 @@ void pcifront_free_roots(struct pcifront
kfree(bus_entry);
}
}
+
+static pci_ers_result_t pcifront_common_process( int cmd, struct
pcifront_device *pdev,
+ pci_channel_state_t state)
+{
+ pci_ers_result_t result;
+ struct pci_driver *pdrv;
+ int bus = pdev->sh_info->aer_op.bus;
+ int devfn = pdev->sh_info->aer_op.devfn;
+ struct pci_dev *pcidev;
+ int flag = 0;
+
+ dev_dbg(&pdev->xdev->dev,
+ "pcifront AER process: cmd %x (bus:%x, devfn%x)",
+ cmd, bus, devfn);
+ result = PCI_ERS_RESULT_NONE;
+
+ pcidev = pci_get_bus_and_slot(bus, devfn);
+ if (!pcidev || !pcidev->driver){
+ dev_err(&pcidev->dev,
+ "device or driver is NULL\n");
+ return result;
+ }
+ pdrv = pcidev->driver;
+
+ if (get_driver(&pdrv->driver)) {
+ if (pdrv->err_handler && pdrv->err_handler->error_detected) {
+ dev_dbg(&pcidev->dev,
+ "trying to call AER service\n");
+ if (pcidev) {
+ flag = 1;
+ switch(cmd) {
+ case XEN_PCI_OP_aer_detected:
+ result =
pdrv->err_handler->error_detected(pcidev, state);
+ break;
+ case XEN_PCI_OP_aer_mmio:
+ result =
pdrv->err_handler->mmio_enabled(pcidev);
+ break;
+ case XEN_PCI_OP_aer_slotreset:
+ result =
pdrv->err_handler->slot_reset(pcidev);
+ break;
+ case XEN_PCI_OP_aer_resume:
+ pdrv->err_handler->resume(pcidev);
+ break;
+ default:
+ dev_err(&pdev->xdev->dev,
+ "bad request in aer recovery
operation!\n");
+
+ }
+ }
+ }
+ put_driver(&pdrv->driver);
+ }
+ if (!flag)
+ result = PCI_ERS_RESULT_NONE;
+
+ return result;
+}
+
+
+void pcifront_do_aer(void *data)
+{
+ struct pcifront_device *pdev = data;
+ int cmd = pdev->sh_info->aer_op.cmd;
+ pci_channel_state_t state =
+ (pci_channel_state_t)pdev->sh_info->aer_op.err;
+
+ /*If a pci_conf op is in progress,
+ we have to wait until it is done before service aer op*/
+ dev_dbg(&pdev->xdev->dev,
+ "pcifront service aer bus %x devfn %x\n",
pdev->sh_info->aer_op.bus,
+ pdev->sh_info->aer_op.devfn);
+
+ pdev->sh_info->aer_op.err = pcifront_common_process(cmd, pdev, state);
+
+ wmb();
+ clear_bit(_XEN_PCIB_active, (unsigned long*)&pdev->sh_info->flags);
+ notify_remote_via_evtchn(pdev->evtchn);
+
+ /*in case of we lost an aer request in four lines time_window*/
+ smp_mb__before_clear_bit();
+ clear_bit( _PDEVB_op_active, &pdev->flags);
+ smp_mb__after_clear_bit();
+
+ schedule_pcifront_aer_op(pdev);
+
+}
+
+irqreturn_t pcifront_handler_aer(int irq, void *dev, struct pt_regs *regs)
+{
+ struct pcifront_device *pdev = dev;
+ schedule_pcifront_aer_op(pdev);
+ return IRQ_HANDLED;
+}
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pcifront/pcifront.h
--- a/drivers/xen/pcifront/pcifront.h Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pcifront/pcifront.h Mon Nov 24 11:04:54 2008 +0000
@@ -10,12 +10,18 @@
#include <linux/pci.h>
#include <xen/xenbus.h>
#include <xen/interface/io/pciif.h>
+#include <linux/interrupt.h>
#include <xen/pcifront.h>
+#include <asm/atomic.h>
+#include <linux/workqueue.h>
struct pci_bus_entry {
struct list_head list;
struct pci_bus *bus;
};
+
+#define _PDEVB_op_active (0)
+#define PDEVB_op_active (1 << (_PDEVB_op_active))
struct pcifront_device {
struct xenbus_device *xdev;
@@ -28,6 +34,9 @@ struct pcifront_device {
/* Lock this when doing any operations in sh_info */
spinlock_t sh_info_lock;
struct xen_pci_sharedinfo *sh_info;
+ struct work_struct op_work;
+ unsigned long flags;
+
};
int pcifront_connect(struct pcifront_device *pdev);
@@ -39,4 +48,8 @@ int pcifront_rescan_root(struct pcifront
unsigned int domain, unsigned int bus);
void pcifront_free_roots(struct pcifront_device *pdev);
+void pcifront_do_aer( void *data);
+
+irqreturn_t pcifront_handler_aer(int irq, void *dev, struct pt_regs *regs);
+
#endif /* __XEN_PCIFRONT_H__ */
diff -r 5888ffa4b252 -r 412b24a36929 drivers/xen/pcifront/xenbus.c
--- a/drivers/xen/pcifront/xenbus.c Mon Nov 24 11:03:21 2008 +0000
+++ b/drivers/xen/pcifront/xenbus.c Mon Nov 24 11:04:54 2008 +0000
@@ -7,6 +7,7 @@
#include <linux/init.h>
#include <linux/mm.h>
#include <xen/xenbus.h>
+#include <xen/evtchn.h>
#include <xen/gnttab.h>
#include "pcifront.h"
@@ -34,6 +35,9 @@ static struct pcifront_device *alloc_pde
}
pdev->sh_info->flags = 0;
+ /*Flag for registering PV AER handler*/
+ set_bit(_XEN_PCIB_AERHANDLER, (void*)&pdev->sh_info->flags);
+
xdev->dev.driver_data = pdev;
pdev->xdev = xdev;
@@ -45,6 +49,8 @@ static struct pcifront_device *alloc_pde
pdev->evtchn = INVALID_EVTCHN;
pdev->gnt_ref = INVALID_GRANT_REF;
+ INIT_WORK(&pdev->op_work, pcifront_do_aer, pdev);
+
dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p pdev->sh_info @ 0x%p\n",
pdev, pdev->sh_info);
out:
@@ -56,6 +62,11 @@ static void free_pdev(struct pcifront_de
dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev);
pcifront_free_roots(pdev);
+
+ /*For PCIE_AER error handling job*/
+ cancel_delayed_work(&pdev->op_work);
+ flush_scheduled_work();
+ unbind_from_irqhandler(pdev->evtchn, pdev);
if (pdev->evtchn != INVALID_EVTCHN)
xenbus_free_evtchn(pdev->xdev, pdev->evtchn);
@@ -83,6 +94,9 @@ static int pcifront_publish_info(struct
err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn);
if (err)
goto out;
+
+ bind_caller_port_to_irqhandler(pdev->evtchn, pcifront_handler_aer,
+ SA_SAMPLE_RANDOM, "pcifront", pdev);
do_publish:
err = xenbus_transaction_start(&trans);
diff -r 5888ffa4b252 -r 412b24a36929 include/xen/interface/io/pciif.h
--- a/include/xen/interface/io/pciif.h Mon Nov 24 11:03:21 2008 +0000
+++ b/include/xen/interface/io/pciif.h Mon Nov 24 11:04:54 2008 +0000
@@ -30,14 +30,22 @@
/* xen_pci_sharedinfo flags */
#define _XEN_PCIF_active (0)
#define XEN_PCIF_active (1<<_XEN_PCI_active)
+#define _XEN_PCIB_AERHANDLER (1)
+#define XEN_PCIB_AERHANDLER (1<<_XEN_PCIB_AERHANDLER)
+#define _XEN_PCIB_active (2)
+#define XEN_PCIB_active (1<<_XEN_PCIB_active)
/* xen_pci_op commands */
-#define XEN_PCI_OP_conf_read (0)
-#define XEN_PCI_OP_conf_write (1)
-#define XEN_PCI_OP_enable_msi (2)
-#define XEN_PCI_OP_disable_msi (3)
-#define XEN_PCI_OP_enable_msix (4)
-#define XEN_PCI_OP_disable_msix (5)
+#define XEN_PCI_OP_conf_read (0)
+#define XEN_PCI_OP_conf_write (1)
+#define XEN_PCI_OP_enable_msi (2)
+#define XEN_PCI_OP_disable_msi (3)
+#define XEN_PCI_OP_enable_msix (4)
+#define XEN_PCI_OP_disable_msix (5)
+#define XEN_PCI_OP_aer_detected (6)
+#define XEN_PCI_OP_aer_resume (7)
+#define XEN_PCI_OP_aer_mmio (8)
+#define XEN_PCI_OP_aer_slotreset (9)
/* xen_pci_op error numbers */
#define XEN_PCI_ERR_success (0)
@@ -82,10 +90,25 @@ struct xen_pci_op {
struct xen_msix_entry msix_entries[SH_INFO_MAX_VEC];
};
+/*used for pcie aer handling*/
+struct xen_pcie_aer_op
+{
+
+ /* IN: what action to perform: XEN_PCI_OP_* */
+ uint32_t cmd;
+ /*IN/OUT: return aer_op result or carry error_detected state as input*/
+ int32_t err;
+
+ /* IN: which device to touch */
+ uint32_t domain; /* PCI Domain/Segment*/
+ uint32_t bus;
+ uint32_t devfn;
+};
struct xen_pci_sharedinfo {
/* flags - XEN_PCIF_* */
uint32_t flags;
struct xen_pci_op op;
+ struct xen_pcie_aer_op aer_op;
};
#endif /* __XEN_PCI_COMMON_H__ */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|