aboutsummaryrefslogtreecommitdiff
path: root/arch/s390/pci/pci.c
diff options
context:
space:
mode:
authorJan Glauber <jang@linux.vnet.ibm.com>2012-12-11 14:53:35 +0100
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2012-12-14 10:01:17 +0100
commitd0b0885316ab7a97cc8a19027905de3ff7bd1e79 (patch)
tree43a490c63baf2da1d4768f7d083e2e9d6cf8467b /arch/s390/pci/pci.c
parent7313264b899bbf3988841296265a6e0e8a7b6521 (diff)
s390/pci: performance statistics and debug infrastructure
Add support for reading the PCI function measurement block counters provided by the hypervisor. Add two s390 debug features, one for critical errors and one for tracing and provide wrappers to log data. Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/pci/pci.c')
-rw-r--r--arch/s390/pci/pci.c73
1 files changed, 70 insertions, 3 deletions
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index 7ed38e5e302..8fa416b8775 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -98,6 +98,10 @@ EXPORT_SYMBOL_GPL(zpci_iomap_start);
static int __read_mostly aisb_max;
static struct kmem_cache *zdev_irq_cache;
+static struct kmem_cache *zdev_fmb_cache;
+
+debug_info_t *pci_debug_msg_id;
+debug_info_t *pci_debug_err_id;
static inline int irq_to_msi_nr(unsigned int irq)
{
@@ -216,6 +220,7 @@ struct mod_pci_args {
u64 base;
u64 limit;
u64 iota;
+ u64 fmb_addr;
};
static int mod_pci(struct zpci_dev *zdev, int fn, u8 dmaas, struct mod_pci_args *args)
@@ -232,6 +237,7 @@ static int mod_pci(struct zpci_dev *zdev, int fn, u8 dmaas, struct mod_pci_args
fib->pba = args->base;
fib->pal = args->limit;
fib->iota = args->iota;
+ fib->fmb_addr = args->fmb_addr;
rc = mpcifc_instr(req, fib);
free_page((unsigned long) fib);
@@ -242,7 +248,7 @@ static int mod_pci(struct zpci_dev *zdev, int fn, u8 dmaas, struct mod_pci_args
int zpci_register_ioat(struct zpci_dev *zdev, u8 dmaas,
u64 base, u64 limit, u64 iota)
{
- struct mod_pci_args args = { base, limit, iota };
+ struct mod_pci_args args = { base, limit, iota, 0 };
WARN_ON_ONCE(iota & 0x3fff);
args.iota |= ZPCI_IOTA_RTTO_FLAG;
@@ -252,7 +258,7 @@ int zpci_register_ioat(struct zpci_dev *zdev, u8 dmaas,
/* Modify PCI: Unregister I/O address translation parameters */
int zpci_unregister_ioat(struct zpci_dev *zdev, u8 dmaas)
{
- struct mod_pci_args args = { 0, 0, 0 };
+ struct mod_pci_args args = { 0, 0, 0, 0 };
return mod_pci(zdev, ZPCI_MOD_FC_DEREG_IOAT, dmaas, &args);
}
@@ -260,11 +266,46 @@ int zpci_unregister_ioat(struct zpci_dev *zdev, u8 dmaas)
/* Modify PCI: Unregister adapter interruptions */
static int zpci_unregister_airq(struct zpci_dev *zdev)
{
- struct mod_pci_args args = { 0, 0, 0 };
+ struct mod_pci_args args = { 0, 0, 0, 0 };
return mod_pci(zdev, ZPCI_MOD_FC_DEREG_INT, 0, &args);
}
+/* Modify PCI: Set PCI function measurement parameters */
+int zpci_fmb_enable_device(struct zpci_dev *zdev)
+{
+ struct mod_pci_args args = { 0, 0, 0, 0 };
+
+ if (zdev->fmb)
+ return -EINVAL;
+
+ zdev->fmb = kmem_cache_alloc(zdev_fmb_cache, GFP_KERNEL);
+ if (!zdev->fmb)
+ return -ENOMEM;
+ memset(zdev->fmb, 0, sizeof(*zdev->fmb));
+ WARN_ON((u64) zdev->fmb & 0xf);
+
+ args.fmb_addr = virt_to_phys(zdev->fmb);
+ return mod_pci(zdev, ZPCI_MOD_FC_SET_MEASURE, 0, &args);
+}
+
+/* Modify PCI: Disable PCI function measurement */
+int zpci_fmb_disable_device(struct zpci_dev *zdev)
+{
+ struct mod_pci_args args = { 0, 0, 0, 0 };
+ int rc;
+
+ if (!zdev->fmb)
+ return -EINVAL;
+
+ /* Function measurement is disabled if fmb address is zero */
+ rc = mod_pci(zdev, ZPCI_MOD_FC_SET_MEASURE, 0, &args);
+
+ kmem_cache_free(zdev_fmb_cache, zdev->fmb);
+ zdev->fmb = NULL;
+ return rc;
+}
+
#define ZPCI_PCIAS_CFGSPC 15
static int zpci_cfg_load(struct zpci_dev *zdev, int offset, u32 *val, u8 len)
@@ -633,6 +674,7 @@ static void zpci_remove_device(struct pci_dev *pdev)
dev_info(&pdev->dev, "Removing device %u\n", zdev->domain);
zdev->state = ZPCI_FN_STATE_CONFIGURED;
zpci_dma_exit_device(zdev);
+ zpci_fmb_disable_device(zdev);
zpci_sysfs_remove_device(&pdev->dev);
zpci_unmap_resources(pdev);
list_del(&zdev->entry); /* can be called from init */
@@ -799,6 +841,16 @@ static void zpci_irq_exit(void)
kfree(bucket);
}
+void zpci_debug_info(struct zpci_dev *zdev, struct seq_file *m)
+{
+ if (!zdev)
+ return;
+
+ seq_printf(m, "global irq retries: %u\n", atomic_read(&irq_retries));
+ seq_printf(m, "aibv[0]:%016lx aibv[1]:%016lx aisb:%016lx\n",
+ get_imap(0)->aibv, get_imap(1)->aibv, *bucket->aisb);
+}
+
static struct resource *zpci_alloc_bus_resource(unsigned long start, unsigned long size,
unsigned long flags, int domain)
{
@@ -994,6 +1046,8 @@ int zpci_scan_device(struct zpci_dev *zdev)
goto out;
}
+ zpci_debug_init_device(zdev);
+ zpci_fmb_enable_device(zdev);
zpci_map_resources(zdev);
pci_bus_add_devices(zdev->bus);
@@ -1020,6 +1074,11 @@ static int zpci_mem_init(void)
if (!zdev_irq_cache)
goto error_zdev;
+ zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb),
+ 16, 0, NULL);
+ if (!zdev_fmb_cache)
+ goto error_fmb;
+
/* TODO: use realloc */
zpci_iomap_start = kzalloc(ZPCI_IOMAP_MAX_ENTRIES * sizeof(*zpci_iomap_start),
GFP_KERNEL);
@@ -1028,6 +1087,8 @@ static int zpci_mem_init(void)
return 0;
error_iomap:
+ kmem_cache_destroy(zdev_fmb_cache);
+error_fmb:
kmem_cache_destroy(zdev_irq_cache);
error_zdev:
return -ENOMEM;
@@ -1037,6 +1098,7 @@ static void zpci_mem_exit(void)
{
kfree(zpci_iomap_start);
kmem_cache_destroy(zdev_irq_cache);
+ kmem_cache_destroy(zdev_fmb_cache);
}
unsigned int pci_probe = 1;
@@ -1066,6 +1128,10 @@ static int __init pci_base_init(void)
test_facility(69), test_facility(70),
test_facility(71));
+ rc = zpci_debug_init();
+ if (rc)
+ return rc;
+
rc = zpci_mem_init();
if (rc)
goto out_mem;
@@ -1098,6 +1164,7 @@ out_irq:
out_hash:
zpci_mem_exit();
out_mem:
+ zpci_debug_exit();
return rc;
}
subsys_initcall(pci_base_init);