1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2013 - 2018 Intel Corporation. */ |
3 | |
4 | #include <linux/list.h> |
5 | #include <linux/errno.h> |
6 | #include <linux/net/intel/i40e_client.h> |
7 | |
8 | #include "i40e.h" |
9 | |
10 | static LIST_HEAD(i40e_devices); |
11 | static DEFINE_MUTEX(i40e_device_mutex); |
12 | DEFINE_IDA(i40e_client_ida); |
13 | |
14 | static int i40e_client_virtchnl_send(struct i40e_info *ldev, |
15 | struct i40e_client *client, |
16 | u32 vf_id, u8 *msg, u16 len); |
17 | |
18 | static int i40e_client_setup_qvlist(struct i40e_info *ldev, |
19 | struct i40e_client *client, |
20 | struct i40e_qvlist_info *qvlist_info); |
21 | |
22 | static void i40e_client_request_reset(struct i40e_info *ldev, |
23 | struct i40e_client *client, |
24 | u32 reset_level); |
25 | |
26 | static int i40e_client_update_vsi_ctxt(struct i40e_info *ldev, |
27 | struct i40e_client *client, |
28 | bool is_vf, u32 vf_id, |
29 | u32 flag, u32 valid_flag); |
30 | |
31 | static struct i40e_ops i40e_lan_ops = { |
32 | .virtchnl_send = i40e_client_virtchnl_send, |
33 | .setup_qvlist = i40e_client_setup_qvlist, |
34 | .request_reset = i40e_client_request_reset, |
35 | .update_vsi_ctxt = i40e_client_update_vsi_ctxt, |
36 | }; |
37 | |
38 | /** |
39 | * i40e_client_get_params - Get the params that can change at runtime |
40 | * @vsi: the VSI with the message |
41 | * @params: client param struct |
42 | * |
43 | **/ |
44 | static |
45 | int i40e_client_get_params(struct i40e_vsi *vsi, struct i40e_params *params) |
46 | { |
47 | struct i40e_dcbx_config *dcb_cfg = &vsi->back->hw.local_dcbx_config; |
48 | int i = 0; |
49 | |
50 | for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { |
51 | u8 tc = dcb_cfg->etscfg.prioritytable[i]; |
52 | u16 qs_handle; |
53 | |
54 | /* If TC is not enabled for VSI use TC0 for UP */ |
55 | if (!(vsi->tc_config.enabled_tc & BIT(tc))) |
56 | tc = 0; |
57 | |
58 | qs_handle = le16_to_cpu(vsi->info.qs_handle[tc]); |
59 | params->qos.prio_qos[i].tc = tc; |
60 | params->qos.prio_qos[i].qs_handle = qs_handle; |
61 | if (qs_handle == I40E_AQ_VSI_QS_HANDLE_INVALID) { |
62 | dev_err(&vsi->back->pdev->dev, "Invalid queue set handle for TC = %d, vsi id = %d\n" , |
63 | tc, vsi->id); |
64 | return -EINVAL; |
65 | } |
66 | } |
67 | |
68 | params->mtu = vsi->netdev->mtu; |
69 | return 0; |
70 | } |
71 | |
72 | /** |
73 | * i40e_notify_client_of_vf_msg - call the client vf message callback |
74 | * @vsi: the VSI with the message |
75 | * @vf_id: the absolute VF id that sent the message |
76 | * @msg: message buffer |
77 | * @len: length of the message |
78 | * |
79 | * If there is a client to this VSI, call the client |
80 | **/ |
81 | void |
82 | i40e_notify_client_of_vf_msg(struct i40e_vsi *vsi, u32 vf_id, u8 *msg, u16 len) |
83 | { |
84 | struct i40e_pf *pf = vsi->back; |
85 | struct i40e_client_instance *cdev = pf->cinst; |
86 | |
87 | if (!cdev || !cdev->client) |
88 | return; |
89 | if (!cdev->client->ops || !cdev->client->ops->virtchnl_receive) { |
90 | dev_dbg(&pf->pdev->dev, |
91 | "Cannot locate client instance virtual channel receive routine\n" ); |
92 | return; |
93 | } |
94 | if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { |
95 | dev_dbg(&pf->pdev->dev, "Client is not open, abort virtchnl_receive\n" ); |
96 | return; |
97 | } |
98 | cdev->client->ops->virtchnl_receive(&cdev->lan_info, cdev->client, |
99 | vf_id, msg, len); |
100 | } |
101 | |
102 | /** |
103 | * i40e_notify_client_of_l2_param_changes - call the client notify callback |
104 | * @vsi: the VSI with l2 param changes |
105 | * |
106 | * If there is a client to this VSI, call the client |
107 | **/ |
108 | void i40e_notify_client_of_l2_param_changes(struct i40e_vsi *vsi) |
109 | { |
110 | struct i40e_pf *pf = vsi->back; |
111 | struct i40e_client_instance *cdev = pf->cinst; |
112 | struct i40e_params params; |
113 | |
114 | if (!cdev || !cdev->client) |
115 | return; |
116 | if (!cdev->client->ops || !cdev->client->ops->l2_param_change) { |
117 | dev_dbg(&vsi->back->pdev->dev, |
118 | "Cannot locate client instance l2_param_change routine\n" ); |
119 | return; |
120 | } |
121 | if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { |
122 | dev_dbg(&vsi->back->pdev->dev, "Client is not open, abort l2 param change\n" ); |
123 | return; |
124 | } |
125 | memset(¶ms, 0, sizeof(params)); |
126 | i40e_client_get_params(vsi, params: ¶ms); |
127 | memcpy(&cdev->lan_info.params, ¶ms, sizeof(struct i40e_params)); |
128 | cdev->client->ops->l2_param_change(&cdev->lan_info, cdev->client, |
129 | ¶ms); |
130 | } |
131 | |
132 | /** |
133 | * i40e_client_release_qvlist - release MSI-X vector mapping for client |
134 | * @ldev: pointer to L2 context. |
135 | * |
136 | **/ |
137 | static void i40e_client_release_qvlist(struct i40e_info *ldev) |
138 | { |
139 | struct i40e_qvlist_info *qvlist_info = ldev->qvlist_info; |
140 | u32 i; |
141 | |
142 | if (!ldev->qvlist_info) |
143 | return; |
144 | |
145 | for (i = 0; i < qvlist_info->num_vectors; i++) { |
146 | struct i40e_pf *pf = ldev->pf; |
147 | struct i40e_qv_info *qv_info; |
148 | u32 reg_idx; |
149 | |
150 | qv_info = &qvlist_info->qv_info[i]; |
151 | if (!qv_info) |
152 | continue; |
153 | reg_idx = I40E_PFINT_LNKLSTN(qv_info->v_idx - 1); |
154 | wr32(&pf->hw, reg_idx, I40E_PFINT_LNKLSTN_FIRSTQ_INDX_MASK); |
155 | } |
156 | kfree(objp: ldev->qvlist_info); |
157 | ldev->qvlist_info = NULL; |
158 | } |
159 | |
160 | /** |
161 | * i40e_notify_client_of_netdev_close - call the client close callback |
162 | * @vsi: the VSI with netdev closed |
163 | * @reset: true when close called due to a reset pending |
164 | * |
165 | * If there is a client to this netdev, call the client with close |
166 | **/ |
167 | void i40e_notify_client_of_netdev_close(struct i40e_vsi *vsi, bool reset) |
168 | { |
169 | struct i40e_pf *pf = vsi->back; |
170 | struct i40e_client_instance *cdev = pf->cinst; |
171 | |
172 | if (!cdev || !cdev->client) |
173 | return; |
174 | if (!cdev->client->ops || !cdev->client->ops->close) { |
175 | dev_dbg(&vsi->back->pdev->dev, |
176 | "Cannot locate client instance close routine\n" ); |
177 | return; |
178 | } |
179 | if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { |
180 | dev_dbg(&pf->pdev->dev, "Client is not open, abort close\n" ); |
181 | return; |
182 | } |
183 | cdev->client->ops->close(&cdev->lan_info, cdev->client, reset); |
184 | clear_bit(nr: __I40E_CLIENT_INSTANCE_OPENED, addr: &cdev->state); |
185 | i40e_client_release_qvlist(ldev: &cdev->lan_info); |
186 | } |
187 | |
188 | /** |
189 | * i40e_notify_client_of_vf_reset - call the client vf reset callback |
190 | * @pf: PF device pointer |
191 | * @vf_id: asolute id of VF being reset |
192 | * |
193 | * If there is a client attached to this PF, notify when a VF is reset |
194 | **/ |
195 | void i40e_notify_client_of_vf_reset(struct i40e_pf *pf, u32 vf_id) |
196 | { |
197 | struct i40e_client_instance *cdev = pf->cinst; |
198 | |
199 | if (!cdev || !cdev->client) |
200 | return; |
201 | if (!cdev->client->ops || !cdev->client->ops->vf_reset) { |
202 | dev_dbg(&pf->pdev->dev, |
203 | "Cannot locate client instance VF reset routine\n" ); |
204 | return; |
205 | } |
206 | if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { |
207 | dev_dbg(&pf->pdev->dev, "Client is not open, abort vf-reset\n" ); |
208 | return; |
209 | } |
210 | cdev->client->ops->vf_reset(&cdev->lan_info, cdev->client, vf_id); |
211 | } |
212 | |
213 | /** |
214 | * i40e_notify_client_of_vf_enable - call the client vf notification callback |
215 | * @pf: PF device pointer |
216 | * @num_vfs: the number of VFs currently enabled, 0 for disable |
217 | * |
218 | * If there is a client attached to this PF, call its VF notification routine |
219 | **/ |
220 | void i40e_notify_client_of_vf_enable(struct i40e_pf *pf, u32 num_vfs) |
221 | { |
222 | struct i40e_client_instance *cdev = pf->cinst; |
223 | |
224 | if (!cdev || !cdev->client) |
225 | return; |
226 | if (!cdev->client->ops || !cdev->client->ops->vf_enable) { |
227 | dev_dbg(&pf->pdev->dev, |
228 | "Cannot locate client instance VF enable routine\n" ); |
229 | return; |
230 | } |
231 | if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, |
232 | &cdev->state)) { |
233 | dev_dbg(&pf->pdev->dev, "Client is not open, abort vf-enable\n" ); |
234 | return; |
235 | } |
236 | cdev->client->ops->vf_enable(&cdev->lan_info, cdev->client, num_vfs); |
237 | } |
238 | |
239 | /** |
240 | * i40e_vf_client_capable - ask the client if it likes the specified VF |
241 | * @pf: PF device pointer |
242 | * @vf_id: the VF in question |
243 | * |
244 | * If there is a client of the specified type attached to this PF, call |
245 | * its vf_capable routine |
246 | **/ |
247 | int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id) |
248 | { |
249 | struct i40e_client_instance *cdev = pf->cinst; |
250 | int capable = false; |
251 | |
252 | if (!cdev || !cdev->client) |
253 | goto out; |
254 | if (!cdev->client->ops || !cdev->client->ops->vf_capable) { |
255 | dev_dbg(&pf->pdev->dev, |
256 | "Cannot locate client instance VF capability routine\n" ); |
257 | goto out; |
258 | } |
259 | if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) |
260 | goto out; |
261 | |
262 | capable = cdev->client->ops->vf_capable(&cdev->lan_info, |
263 | cdev->client, |
264 | vf_id); |
265 | out: |
266 | return capable; |
267 | } |
268 | |
269 | void i40e_client_update_msix_info(struct i40e_pf *pf) |
270 | { |
271 | struct i40e_client_instance *cdev = pf->cinst; |
272 | |
273 | if (!cdev || !cdev->client) |
274 | return; |
275 | |
276 | cdev->lan_info.msix_count = pf->num_iwarp_msix; |
277 | cdev->lan_info.msix_entries = &pf->msix_entries[pf->iwarp_base_vector]; |
278 | } |
279 | |
280 | static void i40e_auxiliary_dev_release(struct device *dev) |
281 | { |
282 | struct i40e_auxiliary_device *i40e_aux_dev = |
283 | container_of(dev, struct i40e_auxiliary_device, aux_dev.dev); |
284 | |
285 | ida_free(&i40e_client_ida, id: i40e_aux_dev->aux_dev.id); |
286 | kfree(objp: i40e_aux_dev); |
287 | } |
288 | |
289 | static int i40e_register_auxiliary_dev(struct i40e_info *ldev, const char *name) |
290 | { |
291 | struct i40e_auxiliary_device *i40e_aux_dev; |
292 | struct pci_dev *pdev = ldev->pcidev; |
293 | struct auxiliary_device *aux_dev; |
294 | int ret; |
295 | |
296 | i40e_aux_dev = kzalloc(size: sizeof(*i40e_aux_dev), GFP_KERNEL); |
297 | if (!i40e_aux_dev) |
298 | return -ENOMEM; |
299 | |
300 | i40e_aux_dev->ldev = ldev; |
301 | |
302 | aux_dev = &i40e_aux_dev->aux_dev; |
303 | aux_dev->name = name; |
304 | aux_dev->dev.parent = &pdev->dev; |
305 | aux_dev->dev.release = i40e_auxiliary_dev_release; |
306 | ldev->aux_dev = aux_dev; |
307 | |
308 | ret = ida_alloc(ida: &i40e_client_ida, GFP_KERNEL); |
309 | if (ret < 0) { |
310 | kfree(objp: i40e_aux_dev); |
311 | return ret; |
312 | } |
313 | aux_dev->id = ret; |
314 | |
315 | ret = auxiliary_device_init(auxdev: aux_dev); |
316 | if (ret < 0) { |
317 | ida_free(&i40e_client_ida, id: aux_dev->id); |
318 | kfree(objp: i40e_aux_dev); |
319 | return ret; |
320 | } |
321 | |
322 | ret = auxiliary_device_add(aux_dev); |
323 | if (ret) { |
324 | auxiliary_device_uninit(auxdev: aux_dev); |
325 | return ret; |
326 | } |
327 | |
328 | return ret; |
329 | } |
330 | |
331 | /** |
332 | * i40e_client_add_instance - add a client instance struct to the instance list |
333 | * @pf: pointer to the board struct |
334 | * |
335 | **/ |
336 | static void i40e_client_add_instance(struct i40e_pf *pf) |
337 | { |
338 | struct i40e_client_instance *cdev = NULL; |
339 | struct netdev_hw_addr *mac = NULL; |
340 | struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; |
341 | |
342 | cdev = kzalloc(size: sizeof(*cdev), GFP_KERNEL); |
343 | if (!cdev) |
344 | return; |
345 | |
346 | cdev->lan_info.pf = (void *)pf; |
347 | cdev->lan_info.netdev = vsi->netdev; |
348 | cdev->lan_info.pcidev = pf->pdev; |
349 | cdev->lan_info.fid = pf->hw.pf_id; |
350 | cdev->lan_info.ftype = I40E_CLIENT_FTYPE_PF; |
351 | cdev->lan_info.hw_addr = pf->hw.hw_addr; |
352 | cdev->lan_info.ops = &i40e_lan_ops; |
353 | cdev->lan_info.version.major = I40E_CLIENT_VERSION_MAJOR; |
354 | cdev->lan_info.version.minor = I40E_CLIENT_VERSION_MINOR; |
355 | cdev->lan_info.version.build = I40E_CLIENT_VERSION_BUILD; |
356 | cdev->lan_info.fw_maj_ver = pf->hw.aq.fw_maj_ver; |
357 | cdev->lan_info.fw_min_ver = pf->hw.aq.fw_min_ver; |
358 | cdev->lan_info.fw_build = pf->hw.aq.fw_build; |
359 | set_bit(nr: __I40E_CLIENT_INSTANCE_NONE, addr: &cdev->state); |
360 | |
361 | if (i40e_client_get_params(vsi, params: &cdev->lan_info.params)) |
362 | goto free_cdev; |
363 | |
364 | mac = list_first_entry(&cdev->lan_info.netdev->dev_addrs.list, |
365 | struct netdev_hw_addr, list); |
366 | if (mac) |
367 | ether_addr_copy(dst: cdev->lan_info.lanmac, src: mac->addr); |
368 | else |
369 | dev_err(&pf->pdev->dev, "MAC address list is empty!\n" ); |
370 | |
371 | pf->cinst = cdev; |
372 | |
373 | cdev->lan_info.msix_count = pf->num_iwarp_msix; |
374 | cdev->lan_info.msix_entries = &pf->msix_entries[pf->iwarp_base_vector]; |
375 | |
376 | if (i40e_register_auxiliary_dev(ldev: &cdev->lan_info, name: "iwarp" )) |
377 | goto free_cdev; |
378 | |
379 | return; |
380 | |
381 | free_cdev: |
382 | kfree(objp: cdev); |
383 | pf->cinst = NULL; |
384 | } |
385 | |
386 | /** |
387 | * i40e_client_del_instance - removes a client instance from the list |
388 | * @pf: pointer to the board struct |
389 | * |
390 | **/ |
391 | static |
392 | void i40e_client_del_instance(struct i40e_pf *pf) |
393 | { |
394 | kfree(objp: pf->cinst); |
395 | pf->cinst = NULL; |
396 | } |
397 | |
398 | /** |
399 | * i40e_client_subtask - client maintenance work |
400 | * @pf: board private structure |
401 | **/ |
402 | void i40e_client_subtask(struct i40e_pf *pf) |
403 | { |
404 | struct i40e_client *client; |
405 | struct i40e_client_instance *cdev; |
406 | struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; |
407 | int ret = 0; |
408 | |
409 | if (!test_and_clear_bit(nr: __I40E_CLIENT_SERVICE_REQUESTED, addr: pf->state)) |
410 | return; |
411 | cdev = pf->cinst; |
412 | |
413 | /* If we're down or resetting, just bail */ |
414 | if (test_bit(__I40E_DOWN, pf->state) || |
415 | test_bit(__I40E_CONFIG_BUSY, pf->state)) |
416 | return; |
417 | |
418 | if (!cdev || !cdev->client) |
419 | return; |
420 | |
421 | client = cdev->client; |
422 | |
423 | /* Here we handle client opens. If the client is down, and |
424 | * the netdev is registered, then open the client. |
425 | */ |
426 | if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { |
427 | if (vsi->netdev_registered && |
428 | client->ops && client->ops->open) { |
429 | set_bit(nr: __I40E_CLIENT_INSTANCE_OPENED, addr: &cdev->state); |
430 | ret = client->ops->open(&cdev->lan_info, client); |
431 | if (ret) { |
432 | /* Remove failed client instance */ |
433 | clear_bit(nr: __I40E_CLIENT_INSTANCE_OPENED, |
434 | addr: &cdev->state); |
435 | return; |
436 | } |
437 | } |
438 | } |
439 | |
440 | /* enable/disable PE TCP_ENA flag based on netdev down/up |
441 | */ |
442 | if (test_bit(__I40E_VSI_DOWN, vsi->state)) |
443 | i40e_client_update_vsi_ctxt(ldev: &cdev->lan_info, client, |
444 | is_vf: 0, vf_id: 0, flag: 0, |
445 | I40E_CLIENT_VSI_FLAG_TCP_ENABLE); |
446 | else |
447 | i40e_client_update_vsi_ctxt(ldev: &cdev->lan_info, client, |
448 | is_vf: 0, vf_id: 0, |
449 | I40E_CLIENT_VSI_FLAG_TCP_ENABLE, |
450 | I40E_CLIENT_VSI_FLAG_TCP_ENABLE); |
451 | } |
452 | |
453 | /** |
454 | * i40e_lan_add_device - add a lan device struct to the list of lan devices |
455 | * @pf: pointer to the board struct |
456 | * |
457 | * Returns 0 on success or none 0 on error |
458 | **/ |
459 | int i40e_lan_add_device(struct i40e_pf *pf) |
460 | { |
461 | struct i40e_device *ldev; |
462 | int ret = 0; |
463 | |
464 | mutex_lock(&i40e_device_mutex); |
465 | list_for_each_entry(ldev, &i40e_devices, list) { |
466 | if (ldev->pf == pf) { |
467 | ret = -EEXIST; |
468 | goto out; |
469 | } |
470 | } |
471 | ldev = kzalloc(size: sizeof(*ldev), GFP_KERNEL); |
472 | if (!ldev) { |
473 | ret = -ENOMEM; |
474 | goto out; |
475 | } |
476 | ldev->pf = pf; |
477 | INIT_LIST_HEAD(list: &ldev->list); |
478 | list_add(new: &ldev->list, head: &i40e_devices); |
479 | dev_info(&pf->pdev->dev, "Added LAN device PF%d bus=0x%02x dev=0x%02x func=0x%02x\n" , |
480 | pf->hw.pf_id, pf->hw.bus.bus_id, |
481 | pf->hw.bus.device, pf->hw.bus.func); |
482 | |
483 | i40e_client_add_instance(pf); |
484 | |
485 | set_bit(nr: __I40E_CLIENT_SERVICE_REQUESTED, addr: pf->state); |
486 | i40e_service_event_schedule(pf); |
487 | |
488 | out: |
489 | mutex_unlock(lock: &i40e_device_mutex); |
490 | return ret; |
491 | } |
492 | |
493 | /** |
494 | * i40e_lan_del_device - removes a lan device from the device list |
495 | * @pf: pointer to the board struct |
496 | * |
497 | * Returns 0 on success or non-0 on error |
498 | **/ |
499 | int i40e_lan_del_device(struct i40e_pf *pf) |
500 | { |
501 | struct auxiliary_device *aux_dev = pf->cinst->lan_info.aux_dev; |
502 | struct i40e_device *ldev, *tmp; |
503 | int ret = -ENODEV; |
504 | |
505 | auxiliary_device_delete(auxdev: aux_dev); |
506 | auxiliary_device_uninit(auxdev: aux_dev); |
507 | |
508 | /* First, remove any client instance. */ |
509 | i40e_client_del_instance(pf); |
510 | |
511 | mutex_lock(&i40e_device_mutex); |
512 | list_for_each_entry_safe(ldev, tmp, &i40e_devices, list) { |
513 | if (ldev->pf == pf) { |
514 | dev_info(&pf->pdev->dev, "Deleted LAN device PF%d bus=0x%02x dev=0x%02x func=0x%02x\n" , |
515 | pf->hw.pf_id, pf->hw.bus.bus_id, |
516 | pf->hw.bus.device, pf->hw.bus.func); |
517 | list_del(entry: &ldev->list); |
518 | kfree(objp: ldev); |
519 | ret = 0; |
520 | break; |
521 | } |
522 | } |
523 | mutex_unlock(lock: &i40e_device_mutex); |
524 | return ret; |
525 | } |
526 | |
527 | /** |
528 | * i40e_client_virtchnl_send - TBD |
529 | * @ldev: pointer to L2 context |
530 | * @client: Client pointer |
531 | * @vf_id: absolute VF identifier |
532 | * @msg: message buffer |
533 | * @len: length of message buffer |
534 | * |
535 | * Return 0 on success or < 0 on error |
536 | **/ |
537 | static int i40e_client_virtchnl_send(struct i40e_info *ldev, |
538 | struct i40e_client *client, |
539 | u32 vf_id, u8 *msg, u16 len) |
540 | { |
541 | struct i40e_pf *pf = ldev->pf; |
542 | struct i40e_hw *hw = &pf->hw; |
543 | int err; |
544 | |
545 | err = i40e_aq_send_msg_to_vf(hw, vfid: vf_id, v_opcode: VIRTCHNL_OP_RDMA, |
546 | v_retval: 0, msg, msglen: len, NULL); |
547 | if (err) |
548 | dev_err(&pf->pdev->dev, "Unable to send iWarp message to VF, error %d, aq status %d\n" , |
549 | err, hw->aq.asq_last_status); |
550 | |
551 | return err; |
552 | } |
553 | |
554 | /** |
555 | * i40e_client_setup_qvlist |
556 | * @ldev: pointer to L2 context. |
557 | * @client: Client pointer. |
558 | * @qvlist_info: queue and vector list |
559 | * |
560 | * Return 0 on success or < 0 on error |
561 | **/ |
562 | static int i40e_client_setup_qvlist(struct i40e_info *ldev, |
563 | struct i40e_client *client, |
564 | struct i40e_qvlist_info *qvlist_info) |
565 | { |
566 | struct i40e_pf *pf = ldev->pf; |
567 | struct i40e_hw *hw = &pf->hw; |
568 | struct i40e_qv_info *qv_info; |
569 | u32 v_idx, i, reg_idx, reg; |
570 | |
571 | ldev->qvlist_info = kzalloc(struct_size(ldev->qvlist_info, qv_info, |
572 | qvlist_info->num_vectors), GFP_KERNEL); |
573 | if (!ldev->qvlist_info) |
574 | return -ENOMEM; |
575 | ldev->qvlist_info->num_vectors = qvlist_info->num_vectors; |
576 | |
577 | for (i = 0; i < qvlist_info->num_vectors; i++) { |
578 | qv_info = &qvlist_info->qv_info[i]; |
579 | if (!qv_info) |
580 | continue; |
581 | v_idx = qv_info->v_idx; |
582 | |
583 | /* Validate vector id belongs to this client */ |
584 | if ((v_idx >= (pf->iwarp_base_vector + pf->num_iwarp_msix)) || |
585 | (v_idx < pf->iwarp_base_vector)) |
586 | goto err; |
587 | |
588 | ldev->qvlist_info->qv_info[i] = *qv_info; |
589 | reg_idx = I40E_PFINT_LNKLSTN(v_idx - 1); |
590 | |
591 | if (qv_info->ceq_idx == I40E_QUEUE_INVALID_IDX) { |
592 | /* Special case - No CEQ mapped on this vector */ |
593 | wr32(hw, reg_idx, I40E_PFINT_LNKLSTN_FIRSTQ_INDX_MASK); |
594 | } else { |
595 | reg = (qv_info->ceq_idx & |
596 | I40E_PFINT_LNKLSTN_FIRSTQ_INDX_MASK) | |
597 | (I40E_QUEUE_TYPE_PE_CEQ << |
598 | I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT); |
599 | wr32(hw, reg_idx, reg); |
600 | |
601 | reg = (I40E_PFINT_CEQCTL_CAUSE_ENA_MASK | |
602 | (v_idx << I40E_PFINT_CEQCTL_MSIX_INDX_SHIFT) | |
603 | (qv_info->itr_idx << |
604 | I40E_PFINT_CEQCTL_ITR_INDX_SHIFT) | |
605 | (I40E_QUEUE_END_OF_LIST << |
606 | I40E_PFINT_CEQCTL_NEXTQ_INDX_SHIFT)); |
607 | wr32(hw, I40E_PFINT_CEQCTL(qv_info->ceq_idx), reg); |
608 | } |
609 | if (qv_info->aeq_idx != I40E_QUEUE_INVALID_IDX) { |
610 | reg = (I40E_PFINT_AEQCTL_CAUSE_ENA_MASK | |
611 | (v_idx << I40E_PFINT_AEQCTL_MSIX_INDX_SHIFT) | |
612 | (qv_info->itr_idx << |
613 | I40E_PFINT_AEQCTL_ITR_INDX_SHIFT)); |
614 | |
615 | wr32(hw, I40E_PFINT_AEQCTL, reg); |
616 | } |
617 | } |
618 | /* Mitigate sync problems with iwarp VF driver */ |
619 | i40e_flush(hw); |
620 | return 0; |
621 | err: |
622 | kfree(objp: ldev->qvlist_info); |
623 | ldev->qvlist_info = NULL; |
624 | return -EINVAL; |
625 | } |
626 | |
627 | /** |
628 | * i40e_client_request_reset |
629 | * @ldev: pointer to L2 context. |
630 | * @client: Client pointer. |
631 | * @reset_level: reset level |
632 | **/ |
633 | static void i40e_client_request_reset(struct i40e_info *ldev, |
634 | struct i40e_client *client, |
635 | u32 reset_level) |
636 | { |
637 | struct i40e_pf *pf = ldev->pf; |
638 | |
639 | switch (reset_level) { |
640 | case I40E_CLIENT_RESET_LEVEL_PF: |
641 | set_bit(nr: __I40E_PF_RESET_REQUESTED, addr: pf->state); |
642 | break; |
643 | case I40E_CLIENT_RESET_LEVEL_CORE: |
644 | set_bit(nr: __I40E_PF_RESET_REQUESTED, addr: pf->state); |
645 | break; |
646 | default: |
647 | dev_warn(&pf->pdev->dev, |
648 | "Client for PF id %d requested an unsupported reset: %d.\n" , |
649 | pf->hw.pf_id, reset_level); |
650 | break; |
651 | } |
652 | |
653 | i40e_service_event_schedule(pf); |
654 | } |
655 | |
656 | /** |
657 | * i40e_client_update_vsi_ctxt |
658 | * @ldev: pointer to L2 context. |
659 | * @client: Client pointer. |
660 | * @is_vf: if this for the VF |
661 | * @vf_id: if is_vf true this carries the vf_id |
662 | * @flag: Any device level setting that needs to be done for PE |
663 | * @valid_flag: Bits in this match up and enable changing of flag bits |
664 | * |
665 | * Return 0 on success or < 0 on error |
666 | **/ |
667 | static int i40e_client_update_vsi_ctxt(struct i40e_info *ldev, |
668 | struct i40e_client *client, |
669 | bool is_vf, u32 vf_id, |
670 | u32 flag, u32 valid_flag) |
671 | { |
672 | struct i40e_pf *pf = ldev->pf; |
673 | struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; |
674 | struct i40e_vsi_context ctxt; |
675 | bool update = true; |
676 | int err; |
677 | |
678 | /* TODO: for now do not allow setting VF's VSI setting */ |
679 | if (is_vf) |
680 | return -EINVAL; |
681 | |
682 | ctxt.seid = pf->main_vsi_seid; |
683 | ctxt.pf_num = pf->hw.pf_id; |
684 | err = i40e_aq_get_vsi_params(hw: &pf->hw, vsi_ctx: &ctxt, NULL); |
685 | ctxt.flags = I40E_AQ_VSI_TYPE_PF; |
686 | if (err) { |
687 | dev_info(&pf->pdev->dev, |
688 | "couldn't get PF vsi config, err %pe aq_err %s\n" , |
689 | ERR_PTR(err), |
690 | i40e_aq_str(&pf->hw, |
691 | pf->hw.aq.asq_last_status)); |
692 | return -ENOENT; |
693 | } |
694 | |
695 | if ((valid_flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE) && |
696 | (flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE)) { |
697 | ctxt.info.valid_sections = |
698 | cpu_to_le16(I40E_AQ_VSI_PROP_QUEUE_OPT_VALID); |
699 | ctxt.info.queueing_opt_flags |= I40E_AQ_VSI_QUE_OPT_TCP_ENA; |
700 | } else if ((valid_flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE) && |
701 | !(flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE)) { |
702 | ctxt.info.valid_sections = |
703 | cpu_to_le16(I40E_AQ_VSI_PROP_QUEUE_OPT_VALID); |
704 | ctxt.info.queueing_opt_flags &= ~I40E_AQ_VSI_QUE_OPT_TCP_ENA; |
705 | } else { |
706 | update = false; |
707 | dev_warn(&pf->pdev->dev, |
708 | "Client for PF id %d request an unsupported Config: %x.\n" , |
709 | pf->hw.pf_id, flag); |
710 | } |
711 | |
712 | if (update) { |
713 | err = i40e_aq_update_vsi_params(hw: &vsi->back->hw, vsi_ctx: &ctxt, NULL); |
714 | if (err) { |
715 | dev_info(&pf->pdev->dev, |
716 | "update VSI ctxt for PE failed, err %pe aq_err %s\n" , |
717 | ERR_PTR(err), |
718 | i40e_aq_str(&pf->hw, |
719 | pf->hw.aq.asq_last_status)); |
720 | } |
721 | } |
722 | return err; |
723 | } |
724 | |
725 | void i40e_client_device_register(struct i40e_info *ldev, struct i40e_client *client) |
726 | { |
727 | struct i40e_pf *pf = ldev->pf; |
728 | |
729 | pf->cinst->client = client; |
730 | set_bit(nr: __I40E_CLIENT_SERVICE_REQUESTED, addr: pf->state); |
731 | i40e_service_event_schedule(pf); |
732 | } |
733 | EXPORT_SYMBOL_GPL(i40e_client_device_register); |
734 | |
735 | void i40e_client_device_unregister(struct i40e_info *ldev) |
736 | { |
737 | struct i40e_pf *pf = ldev->pf; |
738 | struct i40e_client_instance *cdev = pf->cinst; |
739 | |
740 | if (!cdev) |
741 | return; |
742 | |
743 | while (test_and_set_bit(nr: __I40E_SERVICE_SCHED, addr: pf->state)) |
744 | usleep_range(min: 500, max: 1000); |
745 | |
746 | if (test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { |
747 | cdev->client->ops->close(&cdev->lan_info, cdev->client, false); |
748 | clear_bit(nr: __I40E_CLIENT_INSTANCE_OPENED, addr: &cdev->state); |
749 | i40e_client_release_qvlist(ldev: &cdev->lan_info); |
750 | } |
751 | |
752 | pf->cinst->client = NULL; |
753 | clear_bit(nr: __I40E_SERVICE_SCHED, addr: pf->state); |
754 | } |
755 | EXPORT_SYMBOL_GPL(i40e_client_device_unregister); |
756 | |