1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | /* Copyright (c) 2019 Mellanox Technologies */ |
3 | |
4 | #include <linux/mlx5/driver.h> |
5 | #include "mlx5_core.h" |
6 | #include "lib/pci_vsc.h" |
7 | #include "lib/mlx5.h" |
8 | |
9 | #define BAD_ACCESS 0xBADACCE5 |
10 | #define MLX5_PROTECTED_CR_SCAN_CRSPACE 0x7 |
11 | |
12 | static bool mlx5_crdump_enabled(struct mlx5_core_dev *dev) |
13 | { |
14 | return !!dev->priv.health.crdump_size; |
15 | } |
16 | |
17 | static int mlx5_crdump_fill(struct mlx5_core_dev *dev, u32 *cr_data) |
18 | { |
19 | u32 crdump_size = dev->priv.health.crdump_size; |
20 | int i, ret; |
21 | |
22 | for (i = 0; i < (crdump_size / 4); i++) |
23 | cr_data[i] = BAD_ACCESS; |
24 | |
25 | ret = mlx5_vsc_gw_read_block_fast(dev, data: cr_data, length: crdump_size); |
26 | if (ret <= 0) { |
27 | if (ret == 0) |
28 | return -EIO; |
29 | return ret; |
30 | } |
31 | |
32 | if (crdump_size != ret) { |
33 | mlx5_core_warn(dev, "failed to read full dump, read %d out of %u\n" , |
34 | ret, crdump_size); |
35 | return -EINVAL; |
36 | } |
37 | |
38 | return 0; |
39 | } |
40 | |
41 | int mlx5_crdump_collect(struct mlx5_core_dev *dev, u32 *cr_data) |
42 | { |
43 | int ret; |
44 | |
45 | if (!mlx5_crdump_enabled(dev)) |
46 | return -ENODEV; |
47 | |
48 | ret = mlx5_vsc_gw_lock(dev); |
49 | if (ret) { |
50 | mlx5_core_warn(dev, "crdump: failed to lock vsc gw err %d\n" , |
51 | ret); |
52 | return ret; |
53 | } |
54 | /* Verify no other PF is running cr-dump or sw reset */ |
55 | ret = mlx5_vsc_sem_set_space(dev, space: MLX5_SEMAPHORE_SW_RESET, |
56 | state: MLX5_VSC_LOCK); |
57 | if (ret) { |
58 | if (ret == -EBUSY) |
59 | mlx5_core_info(dev, "SW reset semaphore is already in use\n" ); |
60 | else |
61 | mlx5_core_warn(dev, "Failed to lock SW reset semaphore\n" ); |
62 | goto unlock_gw; |
63 | } |
64 | |
65 | ret = mlx5_vsc_gw_set_space(dev, space: MLX5_VSC_SPACE_SCAN_CRSPACE, NULL); |
66 | if (ret) |
67 | goto unlock_sem; |
68 | |
69 | ret = mlx5_crdump_fill(dev, cr_data); |
70 | |
71 | unlock_sem: |
72 | mlx5_vsc_sem_set_space(dev, space: MLX5_SEMAPHORE_SW_RESET, state: MLX5_VSC_UNLOCK); |
73 | unlock_gw: |
74 | mlx5_vsc_gw_unlock(dev); |
75 | return ret; |
76 | } |
77 | |
78 | int mlx5_crdump_enable(struct mlx5_core_dev *dev) |
79 | { |
80 | struct mlx5_priv *priv = &dev->priv; |
81 | u32 space_size; |
82 | int ret; |
83 | |
84 | if (!mlx5_core_is_pf(dev) || !mlx5_vsc_accessible(dev) || |
85 | mlx5_crdump_enabled(dev)) |
86 | return 0; |
87 | |
88 | ret = mlx5_vsc_gw_lock(dev); |
89 | if (ret) |
90 | return ret; |
91 | |
92 | /* Check if space is supported and get space size */ |
93 | ret = mlx5_vsc_gw_set_space(dev, space: MLX5_VSC_SPACE_SCAN_CRSPACE, |
94 | ret_space_size: &space_size); |
95 | if (ret) { |
96 | /* Unlock and mask error since space is not supported */ |
97 | mlx5_vsc_gw_unlock(dev); |
98 | return 0; |
99 | } |
100 | |
101 | if (!space_size) { |
102 | mlx5_core_warn(dev, "Invalid Crspace size, zero\n" ); |
103 | mlx5_vsc_gw_unlock(dev); |
104 | return -EINVAL; |
105 | } |
106 | |
107 | ret = mlx5_vsc_gw_unlock(dev); |
108 | if (ret) |
109 | return ret; |
110 | |
111 | priv->health.crdump_size = space_size; |
112 | return 0; |
113 | } |
114 | |
115 | void mlx5_crdump_disable(struct mlx5_core_dev *dev) |
116 | { |
117 | dev->priv.health.crdump_size = 0; |
118 | } |
119 | |