xref: /illumos-gate/usr/src/uts/common/io/i40e/core/i40e_hmc.c (revision be4e997e05c92f444c81d2d197b79e67ebee2786)
1 /******************************************************************************
2 
3   Copyright (c) 2013-2015, Intel Corporation
4   All rights reserved.
5 
6   Redistribution and use in source and binary forms, with or without
7   modification, are permitted provided that the following conditions are met:
8 
9    1. Redistributions of source code must retain the above copyright notice,
10       this list of conditions and the following disclaimer.
11 
12    2. Redistributions in binary form must reproduce the above copyright
13       notice, this list of conditions and the following disclaimer in the
14       documentation and/or other materials provided with the distribution.
15 
16    3. Neither the name of the Intel Corporation nor the names of its
17       contributors may be used to endorse or promote products derived from
18       this software without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   POSSIBILITY OF SUCH DAMAGE.
31 
32 ******************************************************************************/
33 /*$FreeBSD: head/sys/dev/ixl/i40e_hmc.c 284049 2015-06-05 22:52:42Z jfv $*/
34 
35 #include "i40e_osdep.h"
36 #include "i40e_register.h"
37 #include "i40e_status.h"
38 #include "i40e_alloc.h"
39 #include "i40e_hmc.h"
40 #ifndef I40E_NO_TYPE_HEADER
41 #include "i40e_type.h"
42 #endif
43 
44 /**
45  * i40e_add_sd_table_entry - Adds a segment descriptor to the table
46  * @hw: pointer to our hw struct
47  * @hmc_info: pointer to the HMC configuration information struct
48  * @sd_index: segment descriptor index to manipulate
49  * @type: what type of segment descriptor we're manipulating
50  * @direct_mode_sz: size to alloc in direct mode
51  **/
52 enum i40e_status_code i40e_add_sd_table_entry(struct i40e_hw *hw,
53 					      struct i40e_hmc_info *hmc_info,
54 					      u32 sd_index,
55 					      enum i40e_sd_entry_type type,
56 					      u64 direct_mode_sz)
57 {
58 	enum i40e_status_code ret_code = I40E_SUCCESS;
59 	struct i40e_hmc_sd_entry *sd_entry;
60 	enum   i40e_memory_type mem_type;
61 	bool dma_mem_alloc_done = FALSE;
62 	struct i40e_dma_mem mem;
63 	u64 alloc_len;
64 
65 	if (NULL == hmc_info->sd_table.sd_entry) {
66 		ret_code = I40E_ERR_BAD_PTR;
67 		DEBUGOUT("i40e_add_sd_table_entry: bad sd_entry\n");
68 		goto exit;
69 	}
70 
71 	if (sd_index >= hmc_info->sd_table.sd_cnt) {
72 		ret_code = I40E_ERR_INVALID_SD_INDEX;
73 		DEBUGOUT("i40e_add_sd_table_entry: bad sd_index\n");
74 		goto exit;
75 	}
76 
77 	sd_entry = &hmc_info->sd_table.sd_entry[sd_index];
78 	if (!sd_entry->valid) {
79 		if (I40E_SD_TYPE_PAGED == type) {
80 			mem_type = i40e_mem_pd;
81 			alloc_len = I40E_HMC_PAGED_BP_SIZE;
82 		} else {
83 			mem_type = i40e_mem_bp_jumbo;
84 			alloc_len = direct_mode_sz;
85 		}
86 
87 		/* allocate a 4K pd page or 2M backing page */
88 		ret_code = i40e_allocate_dma_mem(hw, &mem, mem_type, alloc_len,
89 						 I40E_HMC_PD_BP_BUF_ALIGNMENT);
90 		if (ret_code)
91 			goto exit;
92 		dma_mem_alloc_done = TRUE;
93 		if (I40E_SD_TYPE_PAGED == type) {
94 			ret_code = i40e_allocate_virt_mem(hw,
95 					&sd_entry->u.pd_table.pd_entry_virt_mem,
96 					sizeof(struct i40e_hmc_pd_entry) * 512);
97 			if (ret_code)
98 				goto exit;
99 			sd_entry->u.pd_table.pd_entry =
100 				(struct i40e_hmc_pd_entry *)
101 				sd_entry->u.pd_table.pd_entry_virt_mem.va;
102 			i40e_memcpy(&sd_entry->u.pd_table.pd_page_addr,
103 				    &mem, sizeof(struct i40e_dma_mem),
104 				    I40E_NONDMA_TO_NONDMA);
105 		} else {
106 			i40e_memcpy(&sd_entry->u.bp.addr,
107 				    &mem, sizeof(struct i40e_dma_mem),
108 				    I40E_NONDMA_TO_NONDMA);
109 			sd_entry->u.bp.sd_pd_index = sd_index;
110 		}
111 		/* initialize the sd entry */
112 		hmc_info->sd_table.sd_entry[sd_index].entry_type = type;
113 
114 		/* increment the ref count */
115 		I40E_INC_SD_REFCNT(&hmc_info->sd_table);
116 	}
117 	/* Increment backing page reference count */
118 	if (I40E_SD_TYPE_DIRECT == sd_entry->entry_type)
119 		I40E_INC_BP_REFCNT(&sd_entry->u.bp);
120 exit:
121 	if (I40E_SUCCESS != ret_code)
122 		if (dma_mem_alloc_done)
123 			i40e_free_dma_mem(hw, &mem);
124 
125 	return ret_code;
126 }
127 
128 /**
129  * i40e_add_pd_table_entry - Adds page descriptor to the specified table
130  * @hw: pointer to our HW structure
131  * @hmc_info: pointer to the HMC configuration information structure
132  * @pd_index: which page descriptor index to manipulate
133  * @rsrc_pg: if not NULL, use preallocated page instead of allocating new one.
134  *
135  * This function:
136  *	1. Initializes the pd entry
137  *	2. Adds pd_entry in the pd_table
138  *	3. Mark the entry valid in i40e_hmc_pd_entry structure
139  *	4. Initializes the pd_entry's ref count to 1
140  * assumptions:
141  *	1. The memory for pd should be pinned down, physically contiguous and
142  *	   aligned on 4K boundary and zeroed memory.
143  *	2. It should be 4K in size.
144  **/
145 enum i40e_status_code i40e_add_pd_table_entry(struct i40e_hw *hw,
146 					      struct i40e_hmc_info *hmc_info,
147 					      u32 pd_index,
148 					      struct i40e_dma_mem *rsrc_pg)
149 {
150 	enum i40e_status_code ret_code = I40E_SUCCESS;
151 	struct i40e_hmc_pd_table *pd_table;
152 	struct i40e_hmc_pd_entry *pd_entry;
153 	struct i40e_dma_mem mem;
154 	struct i40e_dma_mem *page = &mem;
155 	u32 sd_idx, rel_pd_idx;
156 	u64 *pd_addr;
157 	u64 page_desc;
158 
159 	if (pd_index / I40E_HMC_PD_CNT_IN_SD >= hmc_info->sd_table.sd_cnt) {
160 		ret_code = I40E_ERR_INVALID_PAGE_DESC_INDEX;
161 		DEBUGOUT("i40e_add_pd_table_entry: bad pd_index\n");
162 		goto exit;
163 	}
164 
165 	/* find corresponding sd */
166 	sd_idx = (pd_index / I40E_HMC_PD_CNT_IN_SD);
167 	if (I40E_SD_TYPE_PAGED !=
168 	    hmc_info->sd_table.sd_entry[sd_idx].entry_type)
169 		goto exit;
170 
171 	rel_pd_idx = (pd_index % I40E_HMC_PD_CNT_IN_SD);
172 	pd_table = &hmc_info->sd_table.sd_entry[sd_idx].u.pd_table;
173 	pd_entry = &pd_table->pd_entry[rel_pd_idx];
174 	if (!pd_entry->valid) {
175 		if (rsrc_pg) {
176 			pd_entry->rsrc_pg = TRUE;
177 			page = rsrc_pg;
178 		} else {
179 			/* allocate a 4K backing page */
180 			ret_code = i40e_allocate_dma_mem(hw, page, i40e_mem_bp,
181 						I40E_HMC_PAGED_BP_SIZE,
182 						I40E_HMC_PD_BP_BUF_ALIGNMENT);
183 			if (ret_code)
184 				goto exit;
185 			pd_entry->rsrc_pg = FALSE;
186 		}
187 
188 		i40e_memcpy(&pd_entry->bp.addr, page,
189 			    sizeof(struct i40e_dma_mem), I40E_NONDMA_TO_NONDMA);
190 		pd_entry->bp.sd_pd_index = pd_index;
191 		pd_entry->bp.entry_type = I40E_SD_TYPE_PAGED;
192 		/* Set page address and valid bit */
193 		page_desc = page->pa | 0x1;
194 
195 		pd_addr = (u64 *)pd_table->pd_page_addr.va;
196 		pd_addr += rel_pd_idx;
197 
198 		/* Add the backing page physical address in the pd entry */
199 		i40e_memcpy(pd_addr, &page_desc, sizeof(u64),
200 			    I40E_NONDMA_TO_DMA);
201 
202 		pd_entry->sd_index = sd_idx;
203 		pd_entry->valid = TRUE;
204 		I40E_INC_PD_REFCNT(pd_table);
205 	}
206 	I40E_INC_BP_REFCNT(&pd_entry->bp);
207 exit:
208 	return ret_code;
209 }
210 
211 /**
212  * i40e_remove_pd_bp - remove a backing page from a page descriptor
213  * @hw: pointer to our HW structure
214  * @hmc_info: pointer to the HMC configuration information structure
215  * @idx: the page index
216  * @is_pf: distinguishes a VF from a PF
217  *
218  * This function:
219  *	1. Marks the entry in pd tabe (for paged address mode) or in sd table
220  *	   (for direct address mode) invalid.
221  *	2. Write to register PMPDINV to invalidate the backing page in FV cache
222  *	3. Decrement the ref count for the pd _entry
223  * assumptions:
224  *	1. Caller can deallocate the memory used by backing storage after this
225  *	   function returns.
226  **/
227 enum i40e_status_code i40e_remove_pd_bp(struct i40e_hw *hw,
228 					struct i40e_hmc_info *hmc_info,
229 					u32 idx)
230 {
231 	enum i40e_status_code ret_code = I40E_SUCCESS;
232 	struct i40e_hmc_pd_entry *pd_entry;
233 	struct i40e_hmc_pd_table *pd_table;
234 	struct i40e_hmc_sd_entry *sd_entry;
235 	u32 sd_idx, rel_pd_idx;
236 	u64 *pd_addr;
237 
238 	/* calculate index */
239 	sd_idx = idx / I40E_HMC_PD_CNT_IN_SD;
240 	rel_pd_idx = idx % I40E_HMC_PD_CNT_IN_SD;
241 	if (sd_idx >= hmc_info->sd_table.sd_cnt) {
242 		ret_code = I40E_ERR_INVALID_PAGE_DESC_INDEX;
243 		DEBUGOUT("i40e_remove_pd_bp: bad idx\n");
244 		goto exit;
245 	}
246 	sd_entry = &hmc_info->sd_table.sd_entry[sd_idx];
247 	if (I40E_SD_TYPE_PAGED != sd_entry->entry_type) {
248 		ret_code = I40E_ERR_INVALID_SD_TYPE;
249 		DEBUGOUT("i40e_remove_pd_bp: wrong sd_entry type\n");
250 		goto exit;
251 	}
252 	/* get the entry and decrease its ref counter */
253 	pd_table = &hmc_info->sd_table.sd_entry[sd_idx].u.pd_table;
254 	pd_entry = &pd_table->pd_entry[rel_pd_idx];
255 	I40E_DEC_BP_REFCNT(&pd_entry->bp);
256 	if (pd_entry->bp.ref_cnt)
257 		goto exit;
258 
259 	/* mark the entry invalid */
260 	pd_entry->valid = FALSE;
261 	I40E_DEC_PD_REFCNT(pd_table);
262 	pd_addr = (u64 *)pd_table->pd_page_addr.va;
263 	pd_addr += rel_pd_idx;
264 	i40e_memset(pd_addr, 0, sizeof(u64), I40E_DMA_MEM);
265 	I40E_INVALIDATE_PF_HMC_PD(hw, sd_idx, idx);
266 
267 	/* free memory here */
268 	if (!pd_entry->rsrc_pg)
269 		ret_code = i40e_free_dma_mem(hw, &(pd_entry->bp.addr));
270 	if (I40E_SUCCESS != ret_code)
271 		goto exit;
272 	if (!pd_table->ref_cnt)
273 		i40e_free_virt_mem(hw, &pd_table->pd_entry_virt_mem);
274 exit:
275 	return ret_code;
276 }
277 
278 /**
279  * i40e_prep_remove_sd_bp - Prepares to remove a backing page from a sd entry
280  * @hmc_info: pointer to the HMC configuration information structure
281  * @idx: the page index
282  **/
283 enum i40e_status_code i40e_prep_remove_sd_bp(struct i40e_hmc_info *hmc_info,
284 					     u32 idx)
285 {
286 	enum i40e_status_code ret_code = I40E_SUCCESS;
287 	struct i40e_hmc_sd_entry *sd_entry;
288 
289 	/* get the entry and decrease its ref counter */
290 	sd_entry = &hmc_info->sd_table.sd_entry[idx];
291 	I40E_DEC_BP_REFCNT(&sd_entry->u.bp);
292 	if (sd_entry->u.bp.ref_cnt) {
293 		ret_code = I40E_ERR_NOT_READY;
294 		goto exit;
295 	}
296 	I40E_DEC_SD_REFCNT(&hmc_info->sd_table);
297 
298 	/* mark the entry invalid */
299 	sd_entry->valid = FALSE;
300 exit:
301 	return ret_code;
302 }
303 
304 /**
305  * i40e_remove_sd_bp_new - Removes a backing page from a segment descriptor
306  * @hw: pointer to our hw struct
307  * @hmc_info: pointer to the HMC configuration information structure
308  * @idx: the page index
309  * @is_pf: used to distinguish between VF and PF
310  **/
311 enum i40e_status_code i40e_remove_sd_bp_new(struct i40e_hw *hw,
312 					    struct i40e_hmc_info *hmc_info,
313 					    u32 idx, bool is_pf)
314 {
315 	struct i40e_hmc_sd_entry *sd_entry;
316 
317 	if (!is_pf)
318 		return I40E_NOT_SUPPORTED;
319 
320 	/* get the entry and decrease its ref counter */
321 	sd_entry = &hmc_info->sd_table.sd_entry[idx];
322 	I40E_CLEAR_PF_SD_ENTRY(hw, idx, I40E_SD_TYPE_DIRECT);
323 
324 	return i40e_free_dma_mem(hw, &(sd_entry->u.bp.addr));
325 }
326 
327 /**
328  * i40e_prep_remove_pd_page - Prepares to remove a PD page from sd entry.
329  * @hmc_info: pointer to the HMC configuration information structure
330  * @idx: segment descriptor index to find the relevant page descriptor
331  **/
332 enum i40e_status_code i40e_prep_remove_pd_page(struct i40e_hmc_info *hmc_info,
333 					       u32 idx)
334 {
335 	enum i40e_status_code ret_code = I40E_SUCCESS;
336 	struct i40e_hmc_sd_entry *sd_entry;
337 
338 	sd_entry = &hmc_info->sd_table.sd_entry[idx];
339 
340 	if (sd_entry->u.pd_table.ref_cnt) {
341 		ret_code = I40E_ERR_NOT_READY;
342 		goto exit;
343 	}
344 
345 	/* mark the entry invalid */
346 	sd_entry->valid = FALSE;
347 
348 	I40E_DEC_SD_REFCNT(&hmc_info->sd_table);
349 exit:
350 	return ret_code;
351 }
352 
353 /**
354  * i40e_remove_pd_page_new - Removes a PD page from sd entry.
355  * @hw: pointer to our hw struct
356  * @hmc_info: pointer to the HMC configuration information structure
357  * @idx: segment descriptor index to find the relevant page descriptor
358  * @is_pf: used to distinguish between VF and PF
359  **/
360 enum i40e_status_code i40e_remove_pd_page_new(struct i40e_hw *hw,
361 					      struct i40e_hmc_info *hmc_info,
362 					      u32 idx, bool is_pf)
363 {
364 	struct i40e_hmc_sd_entry *sd_entry;
365 
366 	if (!is_pf)
367 		return I40E_NOT_SUPPORTED;
368 
369 	sd_entry = &hmc_info->sd_table.sd_entry[idx];
370 	I40E_CLEAR_PF_SD_ENTRY(hw, idx, I40E_SD_TYPE_PAGED);
371 
372 	return i40e_free_dma_mem(hw, &(sd_entry->u.pd_table.pd_page_addr));
373 }
374