xref: /linux/drivers/net/ethernet/mellanox/mlx4/crdump.c (revision 9d106c6dd81bb26ad7fc3ee89cb1d62557c8e2c9)
1 /*
2  * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 
33 #include "mlx4.h"
34 
35 #define BAD_ACCESS			0xBADACCE5
36 #define HEALTH_BUFFER_SIZE		0x40
37 #define CR_ENABLE_BIT			swab32(BIT(6))
38 #define CR_ENABLE_BIT_OFFSET		0xF3F04
39 #define MAX_NUM_OF_DUMPS_TO_STORE	(8)
40 
41 static const char * const region_cr_space_str = "cr-space";
42 static const char * const region_fw_health_str = "fw-health";
43 
44 static const struct devlink_region_ops region_cr_space_ops = {
45 	.name = region_cr_space_str,
46 	.destructor = &kvfree,
47 };
48 
49 static const struct devlink_region_ops region_fw_health_ops = {
50 	.name = region_fw_health_str,
51 	.destructor = &kvfree,
52 };
53 
54 /* Set to true in case cr enable bit was set to true before crdump */
55 static bool crdump_enbale_bit_set;
56 
57 static void crdump_enable_crspace_access(struct mlx4_dev *dev,
58 					 u8 __iomem *cr_space)
59 {
60 	/* Get current enable bit value */
61 	crdump_enbale_bit_set =
62 		readl(cr_space + CR_ENABLE_BIT_OFFSET) & CR_ENABLE_BIT;
63 
64 	/* Enable FW CR filter (set bit6 to 0) */
65 	if (crdump_enbale_bit_set)
66 		writel(readl(cr_space + CR_ENABLE_BIT_OFFSET) & ~CR_ENABLE_BIT,
67 		       cr_space + CR_ENABLE_BIT_OFFSET);
68 
69 	/* Enable block volatile crspace accesses */
70 	writel(swab32(1), cr_space + dev->caps.health_buffer_addrs +
71 	       HEALTH_BUFFER_SIZE);
72 }
73 
74 static void crdump_disable_crspace_access(struct mlx4_dev *dev,
75 					  u8 __iomem *cr_space)
76 {
77 	/* Disable block volatile crspace accesses */
78 	writel(0, cr_space + dev->caps.health_buffer_addrs +
79 	       HEALTH_BUFFER_SIZE);
80 
81 	/* Restore FW CR filter value (set bit6 to original value) */
82 	if (crdump_enbale_bit_set)
83 		writel(readl(cr_space + CR_ENABLE_BIT_OFFSET) | CR_ENABLE_BIT,
84 		       cr_space + CR_ENABLE_BIT_OFFSET);
85 }
86 
87 static void mlx4_crdump_collect_crspace(struct mlx4_dev *dev,
88 					u8 __iomem *cr_space,
89 					u32 id)
90 {
91 	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
92 	struct pci_dev *pdev = dev->persist->pdev;
93 	unsigned long cr_res_size;
94 	u8 *crspace_data;
95 	int offset;
96 	int err;
97 
98 	if (!crdump->region_crspace) {
99 		mlx4_err(dev, "crdump: cr-space region is NULL\n");
100 		return;
101 	}
102 
103 	/* Try to collect CR space */
104 	cr_res_size = pci_resource_len(pdev, 0);
105 	crspace_data = kvmalloc(cr_res_size, GFP_KERNEL);
106 	if (crspace_data) {
107 		for (offset = 0; offset < cr_res_size; offset += 4)
108 			*(u32 *)(crspace_data + offset) =
109 					readl(cr_space + offset);
110 
111 		err = devlink_region_snapshot_create(crdump->region_crspace,
112 						     crspace_data, id);
113 		if (err) {
114 			kvfree(crspace_data);
115 			mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
116 				  region_cr_space_str, id, err);
117 		} else {
118 			mlx4_info(dev, "crdump: added snapshot %d to devlink region %s\n",
119 				  id, region_cr_space_str);
120 		}
121 	} else {
122 		mlx4_err(dev, "crdump: Failed to allocate crspace buffer\n");
123 	}
124 }
125 
126 static void mlx4_crdump_collect_fw_health(struct mlx4_dev *dev,
127 					  u8 __iomem *cr_space,
128 					  u32 id)
129 {
130 	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
131 	u8 *health_data;
132 	int offset;
133 	int err;
134 
135 	if (!crdump->region_fw_health) {
136 		mlx4_err(dev, "crdump: fw-health region is NULL\n");
137 		return;
138 	}
139 
140 	/* Try to collect health buffer */
141 	health_data = kvmalloc(HEALTH_BUFFER_SIZE, GFP_KERNEL);
142 	if (health_data) {
143 		u8 __iomem *health_buf_start =
144 				cr_space + dev->caps.health_buffer_addrs;
145 
146 		for (offset = 0; offset < HEALTH_BUFFER_SIZE; offset += 4)
147 			*(u32 *)(health_data + offset) =
148 					readl(health_buf_start + offset);
149 
150 		err = devlink_region_snapshot_create(crdump->region_fw_health,
151 						     health_data, id);
152 		if (err) {
153 			kvfree(health_data);
154 			mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
155 				  region_fw_health_str, id, err);
156 		} else {
157 			mlx4_info(dev, "crdump: added snapshot %d to devlink region %s\n",
158 				  id, region_fw_health_str);
159 		}
160 	} else {
161 		mlx4_err(dev, "crdump: Failed to allocate health buffer\n");
162 	}
163 }
164 
165 int mlx4_crdump_collect(struct mlx4_dev *dev)
166 {
167 	struct devlink *devlink = priv_to_devlink(mlx4_priv(dev));
168 	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
169 	struct pci_dev *pdev = dev->persist->pdev;
170 	unsigned long cr_res_size;
171 	u8 __iomem *cr_space;
172 	int err;
173 	u32 id;
174 
175 	if (!dev->caps.health_buffer_addrs) {
176 		mlx4_info(dev, "crdump: FW doesn't support health buffer access, skipping\n");
177 		return 0;
178 	}
179 
180 	if (!crdump->snapshot_enable) {
181 		mlx4_info(dev, "crdump: devlink snapshot disabled, skipping\n");
182 		return 0;
183 	}
184 
185 	cr_res_size = pci_resource_len(pdev, 0);
186 
187 	cr_space = ioremap(pci_resource_start(pdev, 0), cr_res_size);
188 	if (!cr_space) {
189 		mlx4_err(dev, "crdump: Failed to map pci cr region\n");
190 		return -ENODEV;
191 	}
192 
193 	/* Get the available snapshot ID for the dumps */
194 	err = devlink_region_snapshot_id_get(devlink, &id);
195 	if (err) {
196 		mlx4_err(dev, "crdump: devlink get snapshot id err %d\n", err);
197 		return err;
198 	}
199 
200 	crdump_enable_crspace_access(dev, cr_space);
201 
202 	/* Try to capture dumps */
203 	mlx4_crdump_collect_crspace(dev, cr_space, id);
204 	mlx4_crdump_collect_fw_health(dev, cr_space, id);
205 
206 	/* Release reference on the snapshot id */
207 	devlink_region_snapshot_id_put(devlink, id);
208 
209 	crdump_disable_crspace_access(dev, cr_space);
210 
211 	iounmap(cr_space);
212 	return 0;
213 }
214 
215 int mlx4_crdump_init(struct mlx4_dev *dev)
216 {
217 	struct devlink *devlink = priv_to_devlink(mlx4_priv(dev));
218 	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
219 	struct pci_dev *pdev = dev->persist->pdev;
220 
221 	crdump->snapshot_enable = false;
222 
223 	/* Create cr-space region */
224 	crdump->region_crspace =
225 		devlink_region_create(devlink,
226 				      &region_cr_space_ops,
227 				      MAX_NUM_OF_DUMPS_TO_STORE,
228 				      pci_resource_len(pdev, 0));
229 	if (IS_ERR(crdump->region_crspace))
230 		mlx4_warn(dev, "crdump: create devlink region %s err %ld\n",
231 			  region_cr_space_str,
232 			  PTR_ERR(crdump->region_crspace));
233 
234 	/* Create fw-health region */
235 	crdump->region_fw_health =
236 		devlink_region_create(devlink,
237 				      &region_fw_health_ops,
238 				      MAX_NUM_OF_DUMPS_TO_STORE,
239 				      HEALTH_BUFFER_SIZE);
240 	if (IS_ERR(crdump->region_fw_health))
241 		mlx4_warn(dev, "crdump: create devlink region %s err %ld\n",
242 			  region_fw_health_str,
243 			  PTR_ERR(crdump->region_fw_health));
244 
245 	return 0;
246 }
247 
248 void mlx4_crdump_end(struct mlx4_dev *dev)
249 {
250 	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
251 
252 	devlink_region_destroy(crdump->region_fw_health);
253 	devlink_region_destroy(crdump->region_crspace);
254 }
255