From: Xiongfeng Wang
ohos inclusion
category: bugfix
issue: #I3ZXZF
CVE: NA
-------------------------------------------------------------------------
When I test 'aer-inject' with the following procedures:
1. inject a fatal error into a upstream PCI bridge
2. remove the upstream bridge by sysfs
3. rescan the PCI tree by 'echo 1 > /sys/bus/pci/rescan'
4. execute command 'rmmod aer-inject'
5. remove the upstream bridge by sysfs again
I came across the following Oops.
[ 799.713238] Internal error: Oops: 96000007 [#1] SMP
[ 799.718099] Process bash (pid: 10683, stack limit = 0x00000000125a3b1b)
[ 799.724686] CPU: 108 PID: 10683 Comm: bash Kdump: loaded Not tainted 4.19.36 #2
[ 799.731962] Hardware name: Huawei TaiShan 2280 V2/BC82AMDD, BIOS 1.05 09/18/2019
[ 799.739325] pstate: 40400009 (nZcv daif +PAN -UAO)
[ 799.744104] pc : pci_remove_bus+0xc0/0x1c0
[ 799.748182] lr : pci_remove_bus+0x94/0x1c0
[ 799.752260] sp : ffffa02e335df940
[ 799.755560] x29: ffffa02e335df940 x28: ffff2000088216a8
[ 799.760849] x27: 1ffff405c66bbfbc x26: ffff20000a9518c0
[ 799.766139] x25: ffffa02dea6ec418 x24: 1ffff405bd4dd883
[ 799.771427] x23: ffffa02e72576628 x22: 1ffff405ce4aecc0
[ 799.776715] x21: ffffa02e72576608 x20: ffff200002e75080
[ 799.782003] x19: ffffa02e72576600 x18: 0000000000000000
[ 799.787291] x17: 0000000000000000 x16: 0000000000000000
[ 799.792578] x15: 0000000000000001 x14: dfff200000000000
[ 799.797866] x13: ffff20000a6dfaf0 x12: 0000000000000000
[ 799.803154] x11: 1fffe4000159b217 x10: ffff04000159b217
[ 799.808442] x9 : dfff200000000000 x8 : ffff20000acd90bf
[ 799.813730] x7 : 0000000000000000 x6 : 0000000000000000
[ 799.819017] x5 : 0000000000000001 x4 : 0000000000000000
[ 799.824306] x3 : 1ffff405dbe62603 x2 : 1fffe400005cea11
[ 799.829593] x1 : dfff200000000000 x0 : ffff200002e75088
[ 799.834882] Call trace:
[ 799.837323] pci_remove_bus+0xc0/0x1c0
[ 799.841056] pci_remove_bus_device+0xd0/0x2f0
[ 799.845392] pci_stop_and_remove_bus_device_locked+0x2c/0x40
[ 799.851028] remove_store+0x1b8/0x1d0
[ 799.854679] dev_attr_store+0x60/0x80
[ 799.858330] sysfs_kf_write+0x104/0x170
[ 799.862149] kernfs_fop_write+0x23c/0x430
[ 799.866143] __vfs_write+0xec/0x4e0
[ 799.869615] vfs_write+0x12c/0x3d0
[ 799.873001] ksys_write+0xd0/0x190
[ 799.876389] __arm64_sys_write+0x70/0xa0
[ 799.880298] el0_svc_common+0xfc/0x278
[ 799.884030] el0_svc_handler+0x50/0xc0
[ 799.887764] el0_svc+0x8/0xc
[ 799.890634] Code: d2c40001 f2fbffe1 91002280 d343fc02 (38e16841)
[ 799.896700] kernel fault(0x1) notification starting on CPU 108
It is because when we alloc a new bus in rescanning process, the
'pci_ops' of the newly allocced 'pci_bus' is inherited from its parent
pci bus. Whereas, the 'pci_ops' of the parent bus may be changed to
'aer_inj_pci_ops' in 'aer_inject()'. When we unload the module
'aer_inject', we only restore the 'pci_ops' for the pci bus of the
error-injected device and the root port in 'aer_inject_exit'. After we
have unloaded the module, the 'pci_ops' of the newly allocced pci bus is
still 'aer_inj_pci_ops'. When we access it, an Oops happened.
This patch add a member 'backup_ops' in 'struct pci_bus' to record the
original 'ops'. When we alloc a child pci bus, we assign the
'backup_ops' of the parent bus to the 'ops' of the child bus.
Maybe the best way is to not modify the 'pci_ops' in 'struct pci_bus',
but this will refactor the 'aer_inject' framework a lot. I haven't found
a better way to handle it.
Signed-off-by: Xiongfeng Wang
Reviewed-by: Hanjun Guo
Signed-off-by: yangerkun
Conflicts:
drivers/pci/probe.c
include/linux/pci.h
Signed-off-by: Xiongfeng Wang
Signed-off-by: Chen Jun
Signed-off-by: Yu Changchun
---
drivers/pci/probe.c | 12 +++++++++---
include/linux/pci.h | 1 +
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index ece90a23936d..0187fe60b7d8 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -896,6 +896,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
bus->sysdata = bridge->sysdata;
bus->msi = bridge->msi;
bus->ops = bridge->ops;
+ bus->backup_ops = bus->ops;
bus->number = bus->busn_res.start = bridge->busnr;
#ifdef CONFIG_PCI_DOMAINS_GENERIC
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
@@ -1057,10 +1058,15 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
child->bus_flags = parent->bus_flags;
host = pci_find_host_bridge(parent);
- if (host->child_ops)
+ if (host->child_ops) {
child->ops = host->child_ops;
- else
- child->ops = parent->ops;
+ } else {
+ if (parent->backup_ops)
+ child->ops = parent->backup_ops;
+ else
+ child->ops = parent->ops;
+ }
+ child->backup_ops = child->ops;
/*
* Initialize some portions of the bus device, but don't register
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 22207a79762c..936da8925d68 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -615,6 +615,7 @@ struct pci_bus {
struct resource busn_res; /* Bus numbers routed to this bus */
struct pci_ops *ops; /* Configuration access functions */
+ struct pci_ops *backup_ops;
struct msi_controller *msi; /* MSI controller */
void *sysdata; /* Hook for sys-specific extension */
struct proc_dir_entry *procdir; /* Directory entry in /proc/bus/pci */
--
2.22.0