diff options
Diffstat (limited to 'drivers/pci/pcie/aer/aer_inject.c')
-rw-r--r-- | drivers/pci/pcie/aer/aer_inject.c | 93 |
1 files changed, 70 insertions, 23 deletions
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 62d15f652bb6..f8f425b8731d 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * PCIE AER software error injection support. | 2 | * PCIe AER software error injection support. |
3 | * | 3 | * |
4 | * Debuging PCIE AER code is quite difficult because it is hard to | 4 | * Debuging PCIe AER code is quite difficult because it is hard to |
5 | * trigger various real hardware errors. Software based error | 5 | * trigger various real hardware errors. Software based error |
6 | * injection can fake almost all kinds of errors with the help of a | 6 | * injection can fake almost all kinds of errors with the help of a |
7 | * user space helper tool aer-inject, which can be gotten from: | 7 | * user space helper tool aer-inject, which can be gotten from: |
@@ -21,8 +21,10 @@ | |||
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/miscdevice.h> | 22 | #include <linux/miscdevice.h> |
23 | #include <linux/pci.h> | 23 | #include <linux/pci.h> |
24 | #include <linux/slab.h> | ||
24 | #include <linux/fs.h> | 25 | #include <linux/fs.h> |
25 | #include <linux/uaccess.h> | 26 | #include <linux/uaccess.h> |
27 | #include <linux/stddef.h> | ||
26 | #include "aerdrv.h" | 28 | #include "aerdrv.h" |
27 | 29 | ||
28 | struct aer_error_inj { | 30 | struct aer_error_inj { |
@@ -35,10 +37,12 @@ struct aer_error_inj { | |||
35 | u32 header_log1; | 37 | u32 header_log1; |
36 | u32 header_log2; | 38 | u32 header_log2; |
37 | u32 header_log3; | 39 | u32 header_log3; |
40 | u16 domain; | ||
38 | }; | 41 | }; |
39 | 42 | ||
40 | struct aer_error { | 43 | struct aer_error { |
41 | struct list_head list; | 44 | struct list_head list; |
45 | u16 domain; | ||
42 | unsigned int bus; | 46 | unsigned int bus; |
43 | unsigned int devfn; | 47 | unsigned int devfn; |
44 | int pos_cap_err; | 48 | int pos_cap_err; |
@@ -66,22 +70,27 @@ static LIST_HEAD(pci_bus_ops_list); | |||
66 | /* Protect einjected and pci_bus_ops_list */ | 70 | /* Protect einjected and pci_bus_ops_list */ |
67 | static DEFINE_SPINLOCK(inject_lock); | 71 | static DEFINE_SPINLOCK(inject_lock); |
68 | 72 | ||
69 | static void aer_error_init(struct aer_error *err, unsigned int bus, | 73 | static void aer_error_init(struct aer_error *err, u16 domain, |
70 | unsigned int devfn, int pos_cap_err) | 74 | unsigned int bus, unsigned int devfn, |
75 | int pos_cap_err) | ||
71 | { | 76 | { |
72 | INIT_LIST_HEAD(&err->list); | 77 | INIT_LIST_HEAD(&err->list); |
78 | err->domain = domain; | ||
73 | err->bus = bus; | 79 | err->bus = bus; |
74 | err->devfn = devfn; | 80 | err->devfn = devfn; |
75 | err->pos_cap_err = pos_cap_err; | 81 | err->pos_cap_err = pos_cap_err; |
76 | } | 82 | } |
77 | 83 | ||
78 | /* inject_lock must be held before calling */ | 84 | /* inject_lock must be held before calling */ |
79 | static struct aer_error *__find_aer_error(unsigned int bus, unsigned int devfn) | 85 | static struct aer_error *__find_aer_error(u16 domain, unsigned int bus, |
86 | unsigned int devfn) | ||
80 | { | 87 | { |
81 | struct aer_error *err; | 88 | struct aer_error *err; |
82 | 89 | ||
83 | list_for_each_entry(err, &einjected, list) { | 90 | list_for_each_entry(err, &einjected, list) { |
84 | if (bus == err->bus && devfn == err->devfn) | 91 | if (domain == err->domain && |
92 | bus == err->bus && | ||
93 | devfn == err->devfn) | ||
85 | return err; | 94 | return err; |
86 | } | 95 | } |
87 | return NULL; | 96 | return NULL; |
@@ -90,7 +99,10 @@ static struct aer_error *__find_aer_error(unsigned int bus, unsigned int devfn) | |||
90 | /* inject_lock must be held before calling */ | 99 | /* inject_lock must be held before calling */ |
91 | static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev) | 100 | static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev) |
92 | { | 101 | { |
93 | return __find_aer_error(dev->bus->number, dev->devfn); | 102 | int domain = pci_domain_nr(dev->bus); |
103 | if (domain < 0) | ||
104 | return NULL; | ||
105 | return __find_aer_error((u16)domain, dev->bus->number, dev->devfn); | ||
94 | } | 106 | } |
95 | 107 | ||
96 | /* inject_lock must be held before calling */ | 108 | /* inject_lock must be held before calling */ |
@@ -172,11 +184,15 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where, | |||
172 | struct aer_error *err; | 184 | struct aer_error *err; |
173 | unsigned long flags; | 185 | unsigned long flags; |
174 | struct pci_ops *ops; | 186 | struct pci_ops *ops; |
187 | int domain; | ||
175 | 188 | ||
176 | spin_lock_irqsave(&inject_lock, flags); | 189 | spin_lock_irqsave(&inject_lock, flags); |
177 | if (size != sizeof(u32)) | 190 | if (size != sizeof(u32)) |
178 | goto out; | 191 | goto out; |
179 | err = __find_aer_error(bus->number, devfn); | 192 | domain = pci_domain_nr(bus); |
193 | if (domain < 0) | ||
194 | goto out; | ||
195 | err = __find_aer_error((u16)domain, bus->number, devfn); | ||
180 | if (!err) | 196 | if (!err) |
181 | goto out; | 197 | goto out; |
182 | 198 | ||
@@ -200,11 +216,15 @@ int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, int size, | |||
200 | unsigned long flags; | 216 | unsigned long flags; |
201 | int rw1cs; | 217 | int rw1cs; |
202 | struct pci_ops *ops; | 218 | struct pci_ops *ops; |
219 | int domain; | ||
203 | 220 | ||
204 | spin_lock_irqsave(&inject_lock, flags); | 221 | spin_lock_irqsave(&inject_lock, flags); |
205 | if (size != sizeof(u32)) | 222 | if (size != sizeof(u32)) |
206 | goto out; | 223 | goto out; |
207 | err = __find_aer_error(bus->number, devfn); | 224 | domain = pci_domain_nr(bus); |
225 | if (domain < 0) | ||
226 | goto out; | ||
227 | err = __find_aer_error((u16)domain, bus->number, devfn); | ||
208 | if (!err) | 228 | if (!err) |
209 | goto out; | 229 | goto out; |
210 | 230 | ||
@@ -262,7 +282,7 @@ out: | |||
262 | static struct pci_dev *pcie_find_root_port(struct pci_dev *dev) | 282 | static struct pci_dev *pcie_find_root_port(struct pci_dev *dev) |
263 | { | 283 | { |
264 | while (1) { | 284 | while (1) { |
265 | if (!dev->is_pcie) | 285 | if (!pci_is_pcie(dev)) |
266 | break; | 286 | break; |
267 | if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) | 287 | if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) |
268 | return dev; | 288 | return dev; |
@@ -302,28 +322,31 @@ static int aer_inject(struct aer_error_inj *einj) | |||
302 | unsigned long flags; | 322 | unsigned long flags; |
303 | unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); | 323 | unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); |
304 | int pos_cap_err, rp_pos_cap_err; | 324 | int pos_cap_err, rp_pos_cap_err; |
305 | u32 sever; | 325 | u32 sever, cor_mask, uncor_mask; |
306 | int ret = 0; | 326 | int ret = 0; |
307 | 327 | ||
308 | dev = pci_get_bus_and_slot(einj->bus, devfn); | 328 | dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn); |
309 | if (!dev) | 329 | if (!dev) |
310 | return -EINVAL; | 330 | return -ENODEV; |
311 | rpdev = pcie_find_root_port(dev); | 331 | rpdev = pcie_find_root_port(dev); |
312 | if (!rpdev) { | 332 | if (!rpdev) { |
313 | ret = -EINVAL; | 333 | ret = -ENOTTY; |
314 | goto out_put; | 334 | goto out_put; |
315 | } | 335 | } |
316 | 336 | ||
317 | pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 337 | pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); |
318 | if (!pos_cap_err) { | 338 | if (!pos_cap_err) { |
319 | ret = -EIO; | 339 | ret = -ENOTTY; |
320 | goto out_put; | 340 | goto out_put; |
321 | } | 341 | } |
322 | pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever); | 342 | pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever); |
343 | pci_read_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, &cor_mask); | ||
344 | pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, | ||
345 | &uncor_mask); | ||
323 | 346 | ||
324 | rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); | 347 | rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); |
325 | if (!rp_pos_cap_err) { | 348 | if (!rp_pos_cap_err) { |
326 | ret = -EIO; | 349 | ret = -ENOTTY; |
327 | goto out_put; | 350 | goto out_put; |
328 | } | 351 | } |
329 | 352 | ||
@@ -344,7 +367,8 @@ static int aer_inject(struct aer_error_inj *einj) | |||
344 | if (!err) { | 367 | if (!err) { |
345 | err = err_alloc; | 368 | err = err_alloc; |
346 | err_alloc = NULL; | 369 | err_alloc = NULL; |
347 | aer_error_init(err, einj->bus, devfn, pos_cap_err); | 370 | aer_error_init(err, einj->domain, einj->bus, devfn, |
371 | pos_cap_err); | ||
348 | list_add(&err->list, &einjected); | 372 | list_add(&err->list, &einjected); |
349 | } | 373 | } |
350 | err->uncor_status |= einj->uncor_status; | 374 | err->uncor_status |= einj->uncor_status; |
@@ -354,11 +378,27 @@ static int aer_inject(struct aer_error_inj *einj) | |||
354 | err->header_log2 = einj->header_log2; | 378 | err->header_log2 = einj->header_log2; |
355 | err->header_log3 = einj->header_log3; | 379 | err->header_log3 = einj->header_log3; |
356 | 380 | ||
381 | if (einj->cor_status && !(einj->cor_status & ~cor_mask)) { | ||
382 | ret = -EINVAL; | ||
383 | printk(KERN_WARNING "The correctable error(s) is masked " | ||
384 | "by device\n"); | ||
385 | spin_unlock_irqrestore(&inject_lock, flags); | ||
386 | goto out_put; | ||
387 | } | ||
388 | if (einj->uncor_status && !(einj->uncor_status & ~uncor_mask)) { | ||
389 | ret = -EINVAL; | ||
390 | printk(KERN_WARNING "The uncorrectable error(s) is masked " | ||
391 | "by device\n"); | ||
392 | spin_unlock_irqrestore(&inject_lock, flags); | ||
393 | goto out_put; | ||
394 | } | ||
395 | |||
357 | rperr = __find_aer_error_by_dev(rpdev); | 396 | rperr = __find_aer_error_by_dev(rpdev); |
358 | if (!rperr) { | 397 | if (!rperr) { |
359 | rperr = rperr_alloc; | 398 | rperr = rperr_alloc; |
360 | rperr_alloc = NULL; | 399 | rperr_alloc = NULL; |
361 | aer_error_init(rperr, rpdev->bus->number, rpdev->devfn, | 400 | aer_error_init(rperr, pci_domain_nr(rpdev->bus), |
401 | rpdev->bus->number, rpdev->devfn, | ||
362 | rp_pos_cap_err); | 402 | rp_pos_cap_err); |
363 | list_add(&rperr->list, &einjected); | 403 | list_add(&rperr->list, &einjected); |
364 | } | 404 | } |
@@ -392,8 +432,14 @@ static int aer_inject(struct aer_error_inj *einj) | |||
392 | if (ret) | 432 | if (ret) |
393 | goto out_put; | 433 | goto out_put; |
394 | 434 | ||
395 | if (find_aer_device(rpdev, &edev)) | 435 | if (find_aer_device(rpdev, &edev)) { |
436 | if (!get_service_data(edev)) { | ||
437 | printk(KERN_WARNING "AER service is not initialized\n"); | ||
438 | ret = -EINVAL; | ||
439 | goto out_put; | ||
440 | } | ||
396 | aer_irq(-1, edev); | 441 | aer_irq(-1, edev); |
442 | } | ||
397 | else | 443 | else |
398 | ret = -EINVAL; | 444 | ret = -EINVAL; |
399 | out_put: | 445 | out_put: |
@@ -411,10 +457,11 @@ static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf, | |||
411 | 457 | ||
412 | if (!capable(CAP_SYS_ADMIN)) | 458 | if (!capable(CAP_SYS_ADMIN)) |
413 | return -EPERM; | 459 | return -EPERM; |
414 | 460 | if (usize < offsetof(struct aer_error_inj, domain) || | |
415 | if (usize != sizeof(struct aer_error_inj)) | 461 | usize > sizeof(einj)) |
416 | return -EINVAL; | 462 | return -EINVAL; |
417 | 463 | ||
464 | memset(&einj, 0, sizeof(einj)); | ||
418 | if (copy_from_user(&einj, ubuf, usize)) | 465 | if (copy_from_user(&einj, ubuf, usize)) |
419 | return -EFAULT; | 466 | return -EFAULT; |
420 | 467 | ||
@@ -452,7 +499,7 @@ static void __exit aer_inject_exit(void) | |||
452 | } | 499 | } |
453 | 500 | ||
454 | spin_lock_irqsave(&inject_lock, flags); | 501 | spin_lock_irqsave(&inject_lock, flags); |
455 | list_for_each_entry_safe(err, err_next, &pci_bus_ops_list, list) { | 502 | list_for_each_entry_safe(err, err_next, &einjected, list) { |
456 | list_del(&err->list); | 503 | list_del(&err->list); |
457 | kfree(err); | 504 | kfree(err); |
458 | } | 505 | } |
@@ -462,5 +509,5 @@ static void __exit aer_inject_exit(void) | |||
462 | module_init(aer_inject_init); | 509 | module_init(aer_inject_init); |
463 | module_exit(aer_inject_exit); | 510 | module_exit(aer_inject_exit); |
464 | 511 | ||
465 | MODULE_DESCRIPTION("PCIE AER software error injector"); | 512 | MODULE_DESCRIPTION("PCIe AER software error injector"); |
466 | MODULE_LICENSE("GPL"); | 513 | MODULE_LICENSE("GPL"); |