.. | .. |
---|
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: |
---|