linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] aer inject updates
@ 2018-10-11 16:52 Keith Busch
  2018-10-11 16:52 ` [PATCH 1/3] PCI/AER: Reuse existing pcie_port_find_device() interface Keith Busch
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Keith Busch @ 2018-10-11 16:52 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas; +Cc: Keith Busch

This is an update to the aer inject that was briefly included in tree,
but backed out due to errors in the implementation across various archs.
This series should fix those issues and remove any arch specific
dependencies so it may continue to be used as before. This series was
compile tested using the CROSS_COMPILE build option for ARM.

The first patches two are unchanged from before, but reordered to the
front of the series since they should be non-controversial and and the
AER interrupt handling is actually necessary now that the AER module
uses threaded IRQs.

I moved the ftrace based error injection to be the last patch. I've
updated it to use the portable 'instruction_pointer_set()' function
instead of trying to modify the arch specific pt_regs directly, and
fallback to the existing error injection method if we can't install the
ftrace hooks at run time due to either arch or kernel config limitations.

Keith Busch (3):
  PCI/AER: Reuse existing pcie_port_find_device() interface
  PCI/AER: Abstract AER interrupt handling
  PCI/AER: Covertly inject errors with ftrace hooks

 drivers/pci/pcie/aer.c          |   3 +-
 drivers/pci/pcie/aer_inject.c   | 248 +++++++++++++++++++++++++++++++---------
 drivers/pci/pcie/portdrv.h      |   4 -
 drivers/pci/pcie/portdrv_core.c |   1 +
 4 files changed, 199 insertions(+), 57 deletions(-)

-- 
2.14.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] PCI/AER: Reuse existing pcie_port_find_device() interface
  2018-10-11 16:52 [PATCH 0/3] aer inject updates Keith Busch
@ 2018-10-11 16:52 ` Keith Busch
  2018-10-11 16:52 ` [PATCH 2/3] PCI/AER: Abstract AER interrupt handling Keith Busch
  2018-10-11 16:52 ` [PATCH 3/3] PCI/AER: Covertly inject errors with ftrace hooks Keith Busch
  2 siblings, 0 replies; 6+ messages in thread
From: Keith Busch @ 2018-10-11 16:52 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas; +Cc: Keith Busch

The port services driver already provides a method to find the pcie_device
for a service.  Export that function, use it from the aer_inject module,
and remove the duplicate functionality.

Signed-off-by: Keith Busch <keith.busch@intel.com>
---
 drivers/pci/pcie/aer_inject.c   | 25 ++++---------------------
 drivers/pci/pcie/portdrv_core.c |  1 +
 2 files changed, 5 insertions(+), 21 deletions(-)

diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
index 0eb24346cad3..f40ed5867c89 100644
--- a/drivers/pci/pcie/aer_inject.c
+++ b/drivers/pci/pcie/aer_inject.c
@@ -303,32 +303,13 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
 	return 0;
 }
 
-static int find_aer_device_iter(struct device *device, void *data)
-{
-	struct pcie_device **result = data;
-	struct pcie_device *pcie_dev;
-
-	if (device->bus == &pcie_port_bus_type) {
-		pcie_dev = to_pcie_device(device);
-		if (pcie_dev->service & PCIE_PORT_SERVICE_AER) {
-			*result = pcie_dev;
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static int find_aer_device(struct pci_dev *dev, struct pcie_device **result)
-{
-	return device_for_each_child(&dev->dev, result, find_aer_device_iter);
-}
-
 static int aer_inject(struct aer_error_inj *einj)
 {
 	struct aer_error *err, *rperr;
 	struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
 	struct pci_dev *dev, *rpdev;
 	struct pcie_device *edev;
+	struct device *device;
 	unsigned long flags;
 	unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
 	int pos_cap_err, rp_pos_cap_err;
@@ -464,7 +445,9 @@ static int aer_inject(struct aer_error_inj *einj)
 	if (ret)
 		goto out_put;
 
-	if (find_aer_device(rpdev, &edev)) {
+	device = pcie_port_find_device(rpdev, PCIE_PORT_SERVICE_AER);
+	if (device) {
+		edev = to_pcie_device(device);
 		if (!get_service_data(edev)) {
 			dev_warn(&edev->device,
 				 "aer_inject: AER service is not initialized\n");
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 6542c48c7f59..f458ac9cb70c 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -486,6 +486,7 @@ struct device *pcie_port_find_device(struct pci_dev *dev,
 	device = pdrvs.dev;
 	return device;
 }
+EXPORT_SYMBOL_GPL(pcie_port_find_device);
 
 /**
  * pcie_port_device_remove - unregister PCI Express port service devices
-- 
2.14.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] PCI/AER: Abstract AER interrupt handling
  2018-10-11 16:52 [PATCH 0/3] aer inject updates Keith Busch
  2018-10-11 16:52 ` [PATCH 1/3] PCI/AER: Reuse existing pcie_port_find_device() interface Keith Busch
@ 2018-10-11 16:52 ` Keith Busch
  2018-10-11 16:52 ` [PATCH 3/3] PCI/AER: Covertly inject errors with ftrace hooks Keith Busch
  2 siblings, 0 replies; 6+ messages in thread
From: Keith Busch @ 2018-10-11 16:52 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas; +Cc: Keith Busch

The aer_inject module was directly calling aer_irq().  This required the
AER driver export its private IRQ handler for no other reason than to
support error injection.  A driver should not have to expose its private
interfaces, so use the IRQ subsystem to route injection to the AER driver,
and make aer_irq() a private interface.

This provides additional benefits:

First, directly calling the IRQ handler bypassed the IRQ subsytem so the
injection wasn't really synthesizing what happens if a shared AER interrupt
occurs.

The error injection had to provide the callback data directly, which may be
racing with a removal that is freeing that structure.  The IRQ subsystem
can handle that race.

Finally, using the IRQ subsystem automatically reacts to threaded IRQs,
keeping the error injection abstracted from that implementation detail.

Signed-off-by: Keith Busch <keith.busch@intel.com>
---
 drivers/pci/pcie/aer.c        | 3 +--
 drivers/pci/pcie/aer_inject.c | 5 ++++-
 drivers/pci/pcie/portdrv.h    | 4 ----
 3 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 90b53abf621d..a90a9194ac4a 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -1229,7 +1229,7 @@ static irqreturn_t aer_isr(int irq, void *context)
  *
  * Invoked when Root Port detects AER messages.
  */
-irqreturn_t aer_irq(int irq, void *context)
+static irqreturn_t aer_irq(int irq, void *context)
 {
 	struct pcie_device *pdev = (struct pcie_device *)context;
 	struct aer_rpc *rpc = get_service_data(pdev);
@@ -1249,7 +1249,6 @@ irqreturn_t aer_irq(int irq, void *context)
 
 	return IRQ_WAKE_THREAD;
 }
-EXPORT_SYMBOL_GPL(aer_irq);
 
 static int set_device_error_reporting(struct pci_dev *dev, void *data)
 {
diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
index f40ed5867c89..726987f8d53c 100644
--- a/drivers/pci/pcie/aer_inject.c
+++ b/drivers/pci/pcie/aer_inject.c
@@ -14,6 +14,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/irq.h>
 #include <linux/miscdevice.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
@@ -457,7 +458,9 @@ static int aer_inject(struct aer_error_inj *einj)
 		dev_info(&edev->device,
 			 "aer_inject: Injecting errors %08x/%08x into device %s\n",
 			 einj->cor_status, einj->uncor_status, pci_name(dev));
-		aer_irq(-1, edev);
+		local_irq_disable();
+		generic_handle_irq(edev->irq);
+		local_irq_enable();
 	} else {
 		pci_err(rpdev, "aer_inject: AER device not found\n");
 		ret = -ENODEV;
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index abfdc2ae7979..e495f04394d0 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -151,10 +151,6 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
 }
 #endif
 
-#ifdef CONFIG_PCIEAER
-irqreturn_t aer_irq(int irq, void *context);
-#endif
-
 struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
 							u32 service);
 struct device *pcie_port_find_device(struct pci_dev *dev, u32 service);
-- 
2.14.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] PCI/AER: Covertly inject errors with ftrace hooks
  2018-10-11 16:52 [PATCH 0/3] aer inject updates Keith Busch
  2018-10-11 16:52 ` [PATCH 1/3] PCI/AER: Reuse existing pcie_port_find_device() interface Keith Busch
  2018-10-11 16:52 ` [PATCH 2/3] PCI/AER: Abstract AER interrupt handling Keith Busch
@ 2018-10-11 16:52 ` Keith Busch
  2018-10-11 17:08   ` Bjorn Helgaas
  2 siblings, 1 reply; 6+ messages in thread
From: Keith Busch @ 2018-10-11 16:52 UTC (permalink / raw)
  To: linux-pci, Bjorn Helgaas; +Cc: Keith Busch

The aer_inject module had been intercepting config requests by overwriting
the config accessor operations in the pci_bus ops.  This has several
issues.

First, the module was tracking kernel objects unbeknownst to the drivers
that own them.  The kernel may free those devices, leaving the AER inject
module holding stale references and no way to know that happened.

Second, the PCI enumeration has child devices inherit pci_bus ops from
the parent bus.  Since errors may lead to link resets that trigger
re-enumeration, the child devices would inherit operations that don't
know about the devices using them, causing kernel crashes.

Finally, CONFIG_PCI_LOCKLESS_CONFIG doesn't block accessing the pci_bus
ops, so it's racing with potential in-flight config requests.

This patch uses a different error injection approach leveraging ftrace
to thunk the config space functions.  If the kernel and architecture
are capable, the ftrace hook will overwrite the processor's function
call address with the address of the error injecting function. This way
doesn't modify or track driver structures, fixing the issues with the
current method.

If either the kernel config or platform arch do not support the necessary
ftrace capabilities, the aer_inject module will fallback to the older
way so that it may continue to be used as before.

Signed-off-by: Keith Busch <keith.busch@intel.com>
---
 drivers/pci/pcie/aer_inject.c | 218 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 189 insertions(+), 29 deletions(-)

diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
index 726987f8d53c..4825fa465b60 100644
--- a/drivers/pci/pcie/aer_inject.c
+++ b/drivers/pci/pcie/aer_inject.c
@@ -15,6 +15,10 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/ftrace.h>
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/linkage.h>
 #include <linux/miscdevice.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
@@ -65,6 +69,8 @@ struct pci_bus_ops {
 	struct pci_ops *ops;
 };
 
+static bool legacy;
+
 static LIST_HEAD(einjected);
 
 static LIST_HEAD(pci_bus_ops_list);
@@ -176,14 +182,48 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where,
 	return target;
 }
 
+static int aer_inj_legacy_read(struct pci_bus *bus, unsigned int devfn,
+			       int where, int size, u32 *val)
+{
+	struct pci_ops *ops, *my_ops;
+	int rv;
+
+	ops = __find_pci_bus_ops(bus);
+	if (!ops)
+		return -1;
+
+	my_ops = bus->ops;
+	bus->ops = ops;
+	rv = ops->read(bus, devfn, where, size, val);
+	bus->ops = my_ops;
+
+	return rv;
+}
+
+static int aer_inj_legacy_write(struct pci_bus *bus, unsigned int devfn,
+				int where, int size, u32 val)
+{
+	struct pci_ops *ops, *my_ops;
+	int rv;
+
+	ops = __find_pci_bus_ops(bus);
+	if (!ops)
+		return -1;
+
+	my_ops = bus->ops;
+	bus->ops = ops;
+	rv = ops->write(bus, devfn, where, size, val);
+	bus->ops = my_ops;
+
+	return rv;
+}
+
 static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
 			       int where, int size, u32 *val)
 {
 	u32 *sim;
 	struct aer_error *err;
 	unsigned long flags;
-	struct pci_ops *ops;
-	struct pci_ops *my_ops;
 	int domain;
 	int rv;
 
@@ -204,18 +244,7 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
 		return 0;
 	}
 out:
-	ops = __find_pci_bus_ops(bus);
-	/*
-	 * pci_lock must already be held, so we can directly
-	 * manipulate bus->ops.  Many config access functions,
-	 * including pci_generic_config_read() require the original
-	 * bus->ops be installed to function, so temporarily put them
-	 * back.
-	 */
-	my_ops = bus->ops;
-	bus->ops = ops;
-	rv = ops->read(bus, devfn, where, size, val);
-	bus->ops = my_ops;
+	rv = aer_inj_legacy_read(bus, devfn, where, size, val);
 	spin_unlock_irqrestore(&inject_lock, flags);
 	return rv;
 }
@@ -227,8 +256,6 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
 	struct aer_error *err;
 	unsigned long flags;
 	int rw1cs;
-	struct pci_ops *ops;
-	struct pci_ops *my_ops;
 	int domain;
 	int rv;
 
@@ -252,18 +279,7 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
 		return 0;
 	}
 out:
-	ops = __find_pci_bus_ops(bus);
-	/*
-	 * pci_lock must already be held, so we can directly
-	 * manipulate bus->ops.  Many config access functions,
-	 * including pci_generic_config_write() require the original
-	 * bus->ops be installed to function, so temporarily put them
-	 * back.
-	 */
-	my_ops = bus->ops;
-	bus->ops = ops;
-	rv = ops->write(bus, devfn, where, size, val);
-	bus->ops = my_ops;
+	rv = aer_inj_legacy_write(bus, devfn, where, size, val);
 	spin_unlock_irqrestore(&inject_lock, flags);
 	return rv;
 }
@@ -288,6 +304,9 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
 	struct pci_bus_ops *bus_ops;
 	unsigned long flags;
 
+	if (!legacy)
+		return 0;
+
 	bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL);
 	if (!bus_ops)
 		return -ENOMEM;
@@ -504,9 +523,148 @@ static struct miscdevice aer_inject_device = {
 	.fops = &aer_inject_fops,
 };
 
+static asmlinkage int (*read_config_dword)(struct pci_bus *bus,
+					   unsigned int devfn,
+					   int where, u32 *val);
+static asmlinkage int (*write_config_dword)(struct pci_bus *bus,
+					    unsigned int devfn,
+					    int where, u32 val);
+struct aer_hook {
+	struct ftrace_ops ops;
+	const char *name;
+	void *function;
+	void *original;
+	unsigned long address;
+};
+
+static int asmlinkage ainj_read_config_dword(struct pci_bus *bus,
+			unsigned int devfn, int where, u32 *val)
+{
+	if (!aer_inj_read_config(bus, devfn, where, sizeof(u32), (u32 *)val))
+		return 0;
+	return read_config_dword(bus, devfn, where, val);
+}
+
+static int asmlinkage ainj_write_config_dword(struct pci_bus *bus,
+			unsigned int devfn, int where, u32 val)
+{
+	if (!aer_inj_write_config(bus, devfn, where, sizeof(u32), val))
+		return 0;
+	return write_config_dword(bus, devfn, where, val);
+}
+
+static int aer_inject_resolve_hook_address(struct aer_hook *hook)
+{
+	hook->address = kallsyms_lookup_name(hook->name);
+
+	if (!hook->address) {
+		pr_warn("unresolved symbol: %s\n", hook->name);
+		return -ENOENT;
+	}
+	*((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE;
+
+	return 0;
+}
+
+static void notrace aer_inject_ftrace_thunk(unsigned long ip, unsigned long parent_ip,
+		struct ftrace_ops *ops, struct pt_regs *regs)
+{
+	struct aer_hook *hook = ops->private;
+	instruction_pointer_set(regs, (unsigned long)hook->function);
+}
+
+static int aer_inject_install_hook(struct aer_hook *hook)
+{
+	int err;
+
+	err = aer_inject_resolve_hook_address(hook);
+	if (err)
+		return err;
+
+	hook->ops.private = hook;
+	hook->ops.func = aer_inject_ftrace_thunk;
+	hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS |
+			  FTRACE_OPS_FL_RECURSION_SAFE |
+			  FTRACE_OPS_FL_IPMODIFY;
+	err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
+	if (err) {
+		pr_warn("ftrace_set_filter_ip() failed: %d\n", err);
+		return err;
+	}
+
+	err = register_ftrace_function(&hook->ops);
+	if (err) {
+		pr_warn("register_ftrace_function() failed: %d\n", err);
+		ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
+		return err;
+	}
+	return 0;
+}
+
+static void aer_inject_remove_hook(struct aer_hook *hook)
+{
+	int err;
+
+	err = unregister_ftrace_function(&hook->ops);
+	if (err)
+		pr_warn("unregister_ftrace_function() failed: %d\n", err);
+
+	err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
+	if (err)
+		pr_warn("ftrace_set_filter_ip() failed: %d\n", err);
+}
+
+static int aer_inject_install_hooks(struct aer_hook *hooks, size_t count)
+{
+	int err, i;
+
+	for (i = 0; i < count; i++) {
+		err = aer_inject_install_hook(&hooks[i]);
+		if (err)
+			goto error;
+	}
+	return 0;
+error:
+	for (i--; i >= 0; i--)
+		aer_inject_remove_hook(&hooks[i]);
+	return err;
+}
+
+static void aer_inject_remove_hooks(struct aer_hook *hooks, size_t count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		aer_inject_remove_hook(&hooks[i]);
+}
+
+static struct aer_hook aer_hooks[] = {
+	{
+		.name = "pci_bus_read_config_dword",
+		.function = ainj_read_config_dword,
+		.original = &read_config_dword,
+	},
+	{
+		.name = "pci_bus_write_config_dword",
+		.function = ainj_write_config_dword,
+		.original = &write_config_dword,
+	},
+};
+
 static int __init aer_inject_init(void)
 {
-	return misc_register(&aer_inject_device);
+	int err;
+
+	err = misc_register(&aer_inject_device);
+	if (err)
+		return err;
+
+	err = aer_inject_install_hooks(aer_hooks, ARRAY_SIZE(aer_hooks));
+	if (err) {
+		pr_info("aer_inject: using legacy error inject method\n");
+		legacy = true;
+	}
+	return 0;
 }
 
 static void __exit aer_inject_exit(void)
@@ -515,6 +673,8 @@ static void __exit aer_inject_exit(void)
 	unsigned long flags;
 	struct pci_bus_ops *bus_ops;
 
+	if (!legacy)
+		aer_inject_remove_hooks(aer_hooks, ARRAY_SIZE(aer_hooks));
 	misc_deregister(&aer_inject_device);
 
 	while ((bus_ops = pci_bus_ops_pop())) {
-- 
2.14.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/3] PCI/AER: Covertly inject errors with ftrace hooks
  2018-10-11 16:52 ` [PATCH 3/3] PCI/AER: Covertly inject errors with ftrace hooks Keith Busch
@ 2018-10-11 17:08   ` Bjorn Helgaas
  2018-10-11 17:45     ` Keith Busch
  0 siblings, 1 reply; 6+ messages in thread
From: Bjorn Helgaas @ 2018-10-11 17:08 UTC (permalink / raw)
  To: Keith Busch; +Cc: linux-pci

On Thu, Oct 11, 2018 at 10:52:07AM -0600, Keith Busch wrote:
> The aer_inject module had been intercepting config requests by overwriting
> the config accessor operations in the pci_bus ops.  This has several
> issues.
> 
> First, the module was tracking kernel objects unbeknownst to the drivers
> that own them.  The kernel may free those devices, leaving the AER inject
> module holding stale references and no way to know that happened.
> 
> Second, the PCI enumeration has child devices inherit pci_bus ops from
> the parent bus.  Since errors may lead to link resets that trigger
> re-enumeration, the child devices would inherit operations that don't
> know about the devices using them, causing kernel crashes.
> 
> Finally, CONFIG_PCI_LOCKLESS_CONFIG doesn't block accessing the pci_bus
> ops, so it's racing with potential in-flight config requests.
> 
> This patch uses a different error injection approach leveraging ftrace
> to thunk the config space functions.  If the kernel and architecture
> are capable, the ftrace hook will overwrite the processor's function
> call address with the address of the error injecting function. This way
> doesn't modify or track driver structures, fixing the issues with the
> current method.
> 
> If either the kernel config or platform arch do not support the necessary
> ftrace capabilities, the aer_inject module will fallback to the older
> way so that it may continue to be used as before.
> 
> Signed-off-by: Keith Busch <keith.busch@intel.com>
> ---
>  drivers/pci/pcie/aer_inject.c | 218 ++++++++++++++++++++++++++++++++++++------
>  1 file changed, 189 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
> index 726987f8d53c..4825fa465b60 100644
> --- a/drivers/pci/pcie/aer_inject.c
> +++ b/drivers/pci/pcie/aer_inject.c

> +static int aer_inj_legacy_read(struct pci_bus *bus, unsigned int devfn,
> +			       int where, int size, u32 *val)
> +{
> +	struct pci_ops *ops, *my_ops;
> +	int rv;
> +
> +	ops = __find_pci_bus_ops(bus);
> +	if (!ops)
> +		return -1;
> +
> +	my_ops = bus->ops;
> +	bus->ops = ops;
> +	rv = ops->read(bus, devfn, where, size, val);
> +	bus->ops = my_ops;
> +
> +	return rv;
> +}
> +
> +static int aer_inj_legacy_write(struct pci_bus *bus, unsigned int devfn,
> +				int where, int size, u32 val)
> +{
> +	struct pci_ops *ops, *my_ops;
> +	int rv;
> +
> +	ops = __find_pci_bus_ops(bus);
> +	if (!ops)
> +		return -1;
> +
> +	my_ops = bus->ops;
> +	bus->ops = ops;
> +	rv = ops->write(bus, devfn, where, size, val);
> +	bus->ops = my_ops;
> +
> +	return rv;
> +}

Is there any chance you could split this into two (or more) patches?

The part above looks like pretty straightforward code movement without
any functional change.  It would be nice to get that part out of the way,
then have a smaller patch that adds the fancy ftrace stuff.

>  static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
>  			       int where, int size, u32 *val)
>  {
>  	u32 *sim;
>  	struct aer_error *err;
>  	unsigned long flags;
> -	struct pci_ops *ops;
> -	struct pci_ops *my_ops;
>  	int domain;
>  	int rv;
>  
> @@ -204,18 +244,7 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
>  		return 0;
>  	}
>  out:
> -	ops = __find_pci_bus_ops(bus);
> -	/*
> -	 * pci_lock must already be held, so we can directly
> -	 * manipulate bus->ops.  Many config access functions,
> -	 * including pci_generic_config_read() require the original
> -	 * bus->ops be installed to function, so temporarily put them
> -	 * back.
> -	 */
> -	my_ops = bus->ops;
> -	bus->ops = ops;
> -	rv = ops->read(bus, devfn, where, size, val);
> -	bus->ops = my_ops;
> +	rv = aer_inj_legacy_read(bus, devfn, where, size, val);
>  	spin_unlock_irqrestore(&inject_lock, flags);
>  	return rv;
>  }
> @@ -227,8 +256,6 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
>  	struct aer_error *err;
>  	unsigned long flags;
>  	int rw1cs;
> -	struct pci_ops *ops;
> -	struct pci_ops *my_ops;
>  	int domain;
>  	int rv;
>  
> @@ -252,18 +279,7 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
>  		return 0;
>  	}
>  out:
> -	ops = __find_pci_bus_ops(bus);
> -	/*
> -	 * pci_lock must already be held, so we can directly
> -	 * manipulate bus->ops.  Many config access functions,
> -	 * including pci_generic_config_write() require the original
> -	 * bus->ops be installed to function, so temporarily put them
> -	 * back.
> -	 */
> -	my_ops = bus->ops;
> -	bus->ops = ops;
> -	rv = ops->write(bus, devfn, where, size, val);
> -	bus->ops = my_ops;
> +	rv = aer_inj_legacy_write(bus, devfn, where, size, val);
>  	spin_unlock_irqrestore(&inject_lock, flags);
>  	return rv;
>  }

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/3] PCI/AER: Covertly inject errors with ftrace hooks
  2018-10-11 17:08   ` Bjorn Helgaas
@ 2018-10-11 17:45     ` Keith Busch
  0 siblings, 0 replies; 6+ messages in thread
From: Keith Busch @ 2018-10-11 17:45 UTC (permalink / raw)
  To: Bjorn Helgaas; +Cc: linux-pci

On Thu, Oct 11, 2018 at 12:08:26PM -0500, Bjorn Helgaas wrote:
> On Thu, Oct 11, 2018 at 10:52:07AM -0600, Keith Busch wrote:
> > The aer_inject module had been intercepting config requests by overwriting
> > the config accessor operations in the pci_bus ops.  This has several
> > issues.
> > 
> > First, the module was tracking kernel objects unbeknownst to the drivers
> > that own them.  The kernel may free those devices, leaving the AER inject
> > module holding stale references and no way to know that happened.
> > 
> > Second, the PCI enumeration has child devices inherit pci_bus ops from
> > the parent bus.  Since errors may lead to link resets that trigger
> > re-enumeration, the child devices would inherit operations that don't
> > know about the devices using them, causing kernel crashes.
> > 
> > Finally, CONFIG_PCI_LOCKLESS_CONFIG doesn't block accessing the pci_bus
> > ops, so it's racing with potential in-flight config requests.
> > 
> > This patch uses a different error injection approach leveraging ftrace
> > to thunk the config space functions.  If the kernel and architecture
> > are capable, the ftrace hook will overwrite the processor's function
> > call address with the address of the error injecting function. This way
> > doesn't modify or track driver structures, fixing the issues with the
> > current method.
> > 
> > If either the kernel config or platform arch do not support the necessary
> > ftrace capabilities, the aer_inject module will fallback to the older
> > way so that it may continue to be used as before.
> > 
> > Signed-off-by: Keith Busch <keith.busch@intel.com>
> > ---
> >  drivers/pci/pcie/aer_inject.c | 218 ++++++++++++++++++++++++++++++++++++------
> >  1 file changed, 189 insertions(+), 29 deletions(-)
> > 
> > diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c
> > index 726987f8d53c..4825fa465b60 100644
> > --- a/drivers/pci/pcie/aer_inject.c
> > +++ b/drivers/pci/pcie/aer_inject.c
> 
> > +static int aer_inj_legacy_read(struct pci_bus *bus, unsigned int devfn,
> > +			       int where, int size, u32 *val)
> > +{
> > +	struct pci_ops *ops, *my_ops;
> > +	int rv;
> > +
> > +	ops = __find_pci_bus_ops(bus);
> > +	if (!ops)
> > +		return -1;
> > +
> > +	my_ops = bus->ops;
> > +	bus->ops = ops;
> > +	rv = ops->read(bus, devfn, where, size, val);
> > +	bus->ops = my_ops;
> > +
> > +	return rv;
> > +}
> > +
> > +static int aer_inj_legacy_write(struct pci_bus *bus, unsigned int devfn,
> > +				int where, int size, u32 val)
> > +{
> > +	struct pci_ops *ops, *my_ops;
> > +	int rv;
> > +
> > +	ops = __find_pci_bus_ops(bus);
> > +	if (!ops)
> > +		return -1;
> > +
> > +	my_ops = bus->ops;
> > +	bus->ops = ops;
> > +	rv = ops->write(bus, devfn, where, size, val);
> > +	bus->ops = my_ops;
> > +
> > +	return rv;
> > +}
> 
> Is there any chance you could split this into two (or more) patches?
> 
> The part above looks like pretty straightforward code movement without
> any functional change.  It would be nice to get that part out of the way,
> then have a smaller patch that adds the fancy ftrace stuff.

No problem, will do.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-10-11 17:49 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-11 16:52 [PATCH 0/3] aer inject updates Keith Busch
2018-10-11 16:52 ` [PATCH 1/3] PCI/AER: Reuse existing pcie_port_find_device() interface Keith Busch
2018-10-11 16:52 ` [PATCH 2/3] PCI/AER: Abstract AER interrupt handling Keith Busch
2018-10-11 16:52 ` [PATCH 3/3] PCI/AER: Covertly inject errors with ftrace hooks Keith Busch
2018-10-11 17:08   ` Bjorn Helgaas
2018-10-11 17:45     ` Keith Busch

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).