| .. | .. |
|---|
| 2 | 2 | /* |
|---|
| 3 | 3 | * PCIe AER software error injection support. |
|---|
| 4 | 4 | * |
|---|
| 5 | | - * Debuging PCIe AER code is quite difficult because it is hard to |
|---|
| 5 | + * Debugging PCIe AER code is quite difficult because it is hard to |
|---|
| 6 | 6 | * trigger various real hardware errors. Software based error |
|---|
| 7 | 7 | * injection can fake almost all kinds of errors with the help of a |
|---|
| 8 | 8 | * user space helper tool aer-inject, which can be gotten from: |
|---|
| 9 | | - * http://www.kernel.org/pub/linux/utils/pci/aer-inject/ |
|---|
| 9 | + * https://www.kernel.org/pub/linux/utils/pci/aer-inject/ |
|---|
| 10 | 10 | * |
|---|
| 11 | 11 | * Copyright 2009 Intel Corporation. |
|---|
| 12 | 12 | * Huang Ying <ying.huang@intel.com> |
|---|
| 13 | 13 | */ |
|---|
| 14 | 14 | |
|---|
| 15 | +#define dev_fmt(fmt) "aer_inject: " fmt |
|---|
| 16 | + |
|---|
| 15 | 17 | #include <linux/module.h> |
|---|
| 16 | 18 | #include <linux/init.h> |
|---|
| 19 | +#include <linux/interrupt.h> |
|---|
| 17 | 20 | #include <linux/miscdevice.h> |
|---|
| 18 | 21 | #include <linux/pci.h> |
|---|
| 19 | 22 | #include <linux/slab.h> |
|---|
| .. | .. |
|---|
| 175 | 178 | return target; |
|---|
| 176 | 179 | } |
|---|
| 177 | 180 | |
|---|
| 181 | +static int aer_inj_read(struct pci_bus *bus, unsigned int devfn, int where, |
|---|
| 182 | + int size, u32 *val) |
|---|
| 183 | +{ |
|---|
| 184 | + struct pci_ops *ops, *my_ops; |
|---|
| 185 | + int rv; |
|---|
| 186 | + |
|---|
| 187 | + ops = __find_pci_bus_ops(bus); |
|---|
| 188 | + if (!ops) |
|---|
| 189 | + return -1; |
|---|
| 190 | + |
|---|
| 191 | + my_ops = bus->ops; |
|---|
| 192 | + bus->ops = ops; |
|---|
| 193 | + rv = ops->read(bus, devfn, where, size, val); |
|---|
| 194 | + bus->ops = my_ops; |
|---|
| 195 | + |
|---|
| 196 | + return rv; |
|---|
| 197 | +} |
|---|
| 198 | + |
|---|
| 199 | +static int aer_inj_write(struct pci_bus *bus, unsigned int devfn, int where, |
|---|
| 200 | + int size, u32 val) |
|---|
| 201 | +{ |
|---|
| 202 | + struct pci_ops *ops, *my_ops; |
|---|
| 203 | + int rv; |
|---|
| 204 | + |
|---|
| 205 | + ops = __find_pci_bus_ops(bus); |
|---|
| 206 | + if (!ops) |
|---|
| 207 | + return -1; |
|---|
| 208 | + |
|---|
| 209 | + my_ops = bus->ops; |
|---|
| 210 | + bus->ops = ops; |
|---|
| 211 | + rv = ops->write(bus, devfn, where, size, val); |
|---|
| 212 | + bus->ops = my_ops; |
|---|
| 213 | + |
|---|
| 214 | + return rv; |
|---|
| 215 | +} |
|---|
| 216 | + |
|---|
| 178 | 217 | static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, |
|---|
| 179 | 218 | int where, int size, u32 *val) |
|---|
| 180 | 219 | { |
|---|
| 181 | 220 | u32 *sim; |
|---|
| 182 | 221 | struct aer_error *err; |
|---|
| 183 | 222 | unsigned long flags; |
|---|
| 184 | | - struct pci_ops *ops; |
|---|
| 185 | | - struct pci_ops *my_ops; |
|---|
| 186 | 223 | int domain; |
|---|
| 187 | 224 | int rv; |
|---|
| 188 | 225 | |
|---|
| .. | .. |
|---|
| 203 | 240 | return 0; |
|---|
| 204 | 241 | } |
|---|
| 205 | 242 | out: |
|---|
| 206 | | - ops = __find_pci_bus_ops(bus); |
|---|
| 207 | | - /* |
|---|
| 208 | | - * pci_lock must already be held, so we can directly |
|---|
| 209 | | - * manipulate bus->ops. Many config access functions, |
|---|
| 210 | | - * including pci_generic_config_read() require the original |
|---|
| 211 | | - * bus->ops be installed to function, so temporarily put them |
|---|
| 212 | | - * back. |
|---|
| 213 | | - */ |
|---|
| 214 | | - my_ops = bus->ops; |
|---|
| 215 | | - bus->ops = ops; |
|---|
| 216 | | - rv = ops->read(bus, devfn, where, size, val); |
|---|
| 217 | | - bus->ops = my_ops; |
|---|
| 243 | + rv = aer_inj_read(bus, devfn, where, size, val); |
|---|
| 218 | 244 | spin_unlock_irqrestore(&inject_lock, flags); |
|---|
| 219 | 245 | return rv; |
|---|
| 220 | 246 | } |
|---|
| .. | .. |
|---|
| 226 | 252 | struct aer_error *err; |
|---|
| 227 | 253 | unsigned long flags; |
|---|
| 228 | 254 | int rw1cs; |
|---|
| 229 | | - struct pci_ops *ops; |
|---|
| 230 | | - struct pci_ops *my_ops; |
|---|
| 231 | 255 | int domain; |
|---|
| 232 | 256 | int rv; |
|---|
| 233 | 257 | |
|---|
| .. | .. |
|---|
| 251 | 275 | return 0; |
|---|
| 252 | 276 | } |
|---|
| 253 | 277 | out: |
|---|
| 254 | | - ops = __find_pci_bus_ops(bus); |
|---|
| 255 | | - /* |
|---|
| 256 | | - * pci_lock must already be held, so we can directly |
|---|
| 257 | | - * manipulate bus->ops. Many config access functions, |
|---|
| 258 | | - * including pci_generic_config_write() require the original |
|---|
| 259 | | - * bus->ops be installed to function, so temporarily put them |
|---|
| 260 | | - * back. |
|---|
| 261 | | - */ |
|---|
| 262 | | - my_ops = bus->ops; |
|---|
| 263 | | - bus->ops = ops; |
|---|
| 264 | | - rv = ops->write(bus, devfn, where, size, val); |
|---|
| 265 | | - bus->ops = my_ops; |
|---|
| 278 | + rv = aer_inj_write(bus, devfn, where, size, val); |
|---|
| 266 | 279 | spin_unlock_irqrestore(&inject_lock, flags); |
|---|
| 267 | 280 | return rv; |
|---|
| 268 | 281 | } |
|---|
| .. | .. |
|---|
| 303 | 316 | return 0; |
|---|
| 304 | 317 | } |
|---|
| 305 | 318 | |
|---|
| 306 | | -static int find_aer_device_iter(struct device *device, void *data) |
|---|
| 307 | | -{ |
|---|
| 308 | | - struct pcie_device **result = data; |
|---|
| 309 | | - struct pcie_device *pcie_dev; |
|---|
| 310 | | - |
|---|
| 311 | | - if (device->bus == &pcie_port_bus_type) { |
|---|
| 312 | | - pcie_dev = to_pcie_device(device); |
|---|
| 313 | | - if (pcie_dev->service & PCIE_PORT_SERVICE_AER) { |
|---|
| 314 | | - *result = pcie_dev; |
|---|
| 315 | | - return 1; |
|---|
| 316 | | - } |
|---|
| 317 | | - } |
|---|
| 318 | | - return 0; |
|---|
| 319 | | -} |
|---|
| 320 | | - |
|---|
| 321 | | -static int find_aer_device(struct pci_dev *dev, struct pcie_device **result) |
|---|
| 322 | | -{ |
|---|
| 323 | | - return device_for_each_child(&dev->dev, result, find_aer_device_iter); |
|---|
| 324 | | -} |
|---|
| 325 | | - |
|---|
| 326 | 319 | static int aer_inject(struct aer_error_inj *einj) |
|---|
| 327 | 320 | { |
|---|
| 328 | 321 | struct aer_error *err, *rperr; |
|---|
| 329 | 322 | struct aer_error *err_alloc = NULL, *rperr_alloc = NULL; |
|---|
| 330 | 323 | struct pci_dev *dev, *rpdev; |
|---|
| 331 | 324 | struct pcie_device *edev; |
|---|
| 325 | + struct device *device; |
|---|
| 332 | 326 | unsigned long flags; |
|---|
| 333 | 327 | unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); |
|---|
| 334 | 328 | int pos_cap_err, rp_pos_cap_err; |
|---|
| .. | .. |
|---|
| 340 | 334 | return -ENODEV; |
|---|
| 341 | 335 | rpdev = pcie_find_root_port(dev); |
|---|
| 342 | 336 | if (!rpdev) { |
|---|
| 343 | | - pci_err(dev, "aer_inject: Root port not found\n"); |
|---|
| 337 | + pci_err(dev, "Root port not found\n"); |
|---|
| 344 | 338 | ret = -ENODEV; |
|---|
| 345 | 339 | goto out_put; |
|---|
| 346 | 340 | } |
|---|
| 347 | 341 | |
|---|
| 348 | 342 | pos_cap_err = dev->aer_cap; |
|---|
| 349 | 343 | if (!pos_cap_err) { |
|---|
| 350 | | - pci_err(dev, "aer_inject: Device doesn't support AER\n"); |
|---|
| 344 | + pci_err(dev, "Device doesn't support AER\n"); |
|---|
| 351 | 345 | ret = -EPROTONOSUPPORT; |
|---|
| 352 | 346 | goto out_put; |
|---|
| 353 | 347 | } |
|---|
| .. | .. |
|---|
| 358 | 352 | |
|---|
| 359 | 353 | rp_pos_cap_err = rpdev->aer_cap; |
|---|
| 360 | 354 | if (!rp_pos_cap_err) { |
|---|
| 361 | | - pci_err(rpdev, "aer_inject: Root port doesn't support AER\n"); |
|---|
| 355 | + pci_err(rpdev, "Root port doesn't support AER\n"); |
|---|
| 362 | 356 | ret = -EPROTONOSUPPORT; |
|---|
| 363 | 357 | goto out_put; |
|---|
| 364 | 358 | } |
|---|
| .. | .. |
|---|
| 406 | 400 | if (!aer_mask_override && einj->cor_status && |
|---|
| 407 | 401 | !(einj->cor_status & ~cor_mask)) { |
|---|
| 408 | 402 | ret = -EINVAL; |
|---|
| 409 | | - pci_warn(dev, "aer_inject: The correctable error(s) is masked by device\n"); |
|---|
| 403 | + pci_warn(dev, "The correctable error(s) is masked by device\n"); |
|---|
| 410 | 404 | spin_unlock_irqrestore(&inject_lock, flags); |
|---|
| 411 | 405 | goto out_put; |
|---|
| 412 | 406 | } |
|---|
| 413 | 407 | if (!aer_mask_override && einj->uncor_status && |
|---|
| 414 | 408 | !(einj->uncor_status & ~uncor_mask)) { |
|---|
| 415 | 409 | ret = -EINVAL; |
|---|
| 416 | | - pci_warn(dev, "aer_inject: The uncorrectable error(s) is masked by device\n"); |
|---|
| 410 | + pci_warn(dev, "The uncorrectable error(s) is masked by device\n"); |
|---|
| 417 | 411 | spin_unlock_irqrestore(&inject_lock, flags); |
|---|
| 418 | 412 | goto out_put; |
|---|
| 419 | 413 | } |
|---|
| .. | .. |
|---|
| 464 | 458 | if (ret) |
|---|
| 465 | 459 | goto out_put; |
|---|
| 466 | 460 | |
|---|
| 467 | | - if (find_aer_device(rpdev, &edev)) { |
|---|
| 461 | + device = pcie_port_find_device(rpdev, PCIE_PORT_SERVICE_AER); |
|---|
| 462 | + if (device) { |
|---|
| 463 | + edev = to_pcie_device(device); |
|---|
| 468 | 464 | if (!get_service_data(edev)) { |
|---|
| 469 | | - dev_warn(&edev->device, |
|---|
| 470 | | - "aer_inject: AER service is not initialized\n"); |
|---|
| 465 | + pci_warn(edev->port, "AER service is not initialized\n"); |
|---|
| 471 | 466 | ret = -EPROTONOSUPPORT; |
|---|
| 472 | 467 | goto out_put; |
|---|
| 473 | 468 | } |
|---|
| 474 | | - dev_info(&edev->device, |
|---|
| 475 | | - "aer_inject: Injecting errors %08x/%08x into device %s\n", |
|---|
| 469 | + pci_info(edev->port, "Injecting errors %08x/%08x into device %s\n", |
|---|
| 476 | 470 | einj->cor_status, einj->uncor_status, pci_name(dev)); |
|---|
| 477 | | - aer_irq(-1, edev); |
|---|
| 471 | + ret = irq_inject_interrupt(edev->irq); |
|---|
| 478 | 472 | } else { |
|---|
| 479 | | - pci_err(rpdev, "aer_inject: AER device not found\n"); |
|---|
| 473 | + pci_err(rpdev, "AER device not found\n"); |
|---|
| 480 | 474 | ret = -ENODEV; |
|---|
| 481 | 475 | } |
|---|
| 482 | 476 | out_put: |
|---|