1 | /* |
2 | * Copyright (c) 2012 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 | /*This file support the handling of the Alias GUID feature. */ |
34 | /***********************************************************/ |
35 | #include <rdma/ib_mad.h> |
36 | #include <rdma/ib_smi.h> |
37 | #include <rdma/ib_cache.h> |
38 | #include <rdma/ib_sa.h> |
39 | #include <rdma/ib_pack.h> |
40 | #include <linux/mlx4/cmd.h> |
41 | #include <linux/init.h> |
42 | #include <linux/errno.h> |
43 | #include <rdma/ib_user_verbs.h> |
44 | #include <linux/delay.h> |
45 | #include "mlx4_ib.h" |
46 | |
47 | /* |
48 | The driver keeps the current state of all guids, as they are in the HW. |
49 | Whenever we receive an smp mad GUIDInfo record, the data will be cached. |
50 | */ |
51 | |
52 | struct mlx4_alias_guid_work_context { |
53 | u8 port; |
54 | struct mlx4_ib_dev *dev ; |
55 | struct ib_sa_query *sa_query; |
56 | struct completion done; |
57 | int query_id; |
58 | struct list_head list; |
59 | int block_num; |
60 | ib_sa_comp_mask guid_indexes; |
61 | u8 method; |
62 | }; |
63 | |
64 | struct mlx4_next_alias_guid_work { |
65 | u8 port; |
66 | u8 block_num; |
67 | u8 method; |
68 | struct mlx4_sriov_alias_guid_info_rec_det rec_det; |
69 | }; |
70 | |
71 | static int get_low_record_time_index(struct mlx4_ib_dev *dev, u8 port, |
72 | int *resched_delay_sec); |
73 | |
74 | void mlx4_ib_update_cache_on_guid_change(struct mlx4_ib_dev *dev, int block_num, |
75 | u32 port_num, u8 *p_data) |
76 | { |
77 | int i; |
78 | u64 guid_indexes; |
79 | int slave_id; |
80 | u32 port_index = port_num - 1; |
81 | |
82 | if (!mlx4_is_master(dev: dev->dev)) |
83 | return; |
84 | |
85 | guid_indexes = be64_to_cpu((__force __be64) dev->sriov.alias_guid. |
86 | ports_guid[port_num - 1]. |
87 | all_rec_per_port[block_num].guid_indexes); |
88 | pr_debug("port: %u, guid_indexes: 0x%llx\n" , port_num, guid_indexes); |
89 | |
90 | for (i = 0; i < NUM_ALIAS_GUID_IN_REC; i++) { |
91 | /* The location of the specific index starts from bit number 4 |
92 | * until bit num 11 */ |
93 | if (test_bit(i + 4, (unsigned long *)&guid_indexes)) { |
94 | slave_id = (block_num * NUM_ALIAS_GUID_IN_REC) + i ; |
95 | if (slave_id >= dev->dev->num_slaves) { |
96 | pr_debug("The last slave: %d\n" , slave_id); |
97 | return; |
98 | } |
99 | |
100 | /* cache the guid: */ |
101 | memcpy(&dev->sriov.demux[port_index].guid_cache[slave_id], |
102 | &p_data[i * GUID_REC_SIZE], |
103 | GUID_REC_SIZE); |
104 | } else |
105 | pr_debug("Guid number: %d in block: %d" |
106 | " was not updated\n" , i, block_num); |
107 | } |
108 | } |
109 | |
110 | static __be64 get_cached_alias_guid(struct mlx4_ib_dev *dev, int port, int index) |
111 | { |
112 | if (index >= NUM_ALIAS_GUID_PER_PORT) { |
113 | pr_err("%s: ERROR: asked for index:%d\n" , __func__, index); |
114 | return (__force __be64) -1; |
115 | } |
116 | return *(__be64 *)&dev->sriov.demux[port - 1].guid_cache[index]; |
117 | } |
118 | |
119 | |
120 | ib_sa_comp_mask mlx4_ib_get_aguid_comp_mask_from_ix(int index) |
121 | { |
122 | return IB_SA_COMP_MASK(4 + index); |
123 | } |
124 | |
125 | void mlx4_ib_slave_alias_guid_event(struct mlx4_ib_dev *dev, int slave, |
126 | int port, int slave_init) |
127 | { |
128 | __be64 curr_guid, required_guid; |
129 | int record_num = slave / 8; |
130 | int index = slave % 8; |
131 | int port_index = port - 1; |
132 | unsigned long flags; |
133 | int do_work = 0; |
134 | |
135 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags); |
136 | if (dev->sriov.alias_guid.ports_guid[port_index].state_flags & |
137 | GUID_STATE_NEED_PORT_INIT) |
138 | goto unlock; |
139 | if (!slave_init) { |
140 | curr_guid = *(__be64 *)&dev->sriov. |
141 | alias_guid.ports_guid[port_index]. |
142 | all_rec_per_port[record_num]. |
143 | all_recs[GUID_REC_SIZE * index]; |
144 | if (curr_guid == cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL) || |
145 | !curr_guid) |
146 | goto unlock; |
147 | required_guid = cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL); |
148 | } else { |
149 | required_guid = mlx4_get_admin_guid(dev: dev->dev, entry: slave, port); |
150 | if (required_guid == cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL)) |
151 | goto unlock; |
152 | } |
153 | *(__be64 *)&dev->sriov.alias_guid.ports_guid[port_index]. |
154 | all_rec_per_port[record_num]. |
155 | all_recs[GUID_REC_SIZE * index] = required_guid; |
156 | dev->sriov.alias_guid.ports_guid[port_index]. |
157 | all_rec_per_port[record_num].guid_indexes |
158 | |= mlx4_ib_get_aguid_comp_mask_from_ix(index); |
159 | dev->sriov.alias_guid.ports_guid[port_index]. |
160 | all_rec_per_port[record_num].status |
161 | = MLX4_GUID_INFO_STATUS_IDLE; |
162 | /* set to run immediately */ |
163 | dev->sriov.alias_guid.ports_guid[port_index]. |
164 | all_rec_per_port[record_num].time_to_run = 0; |
165 | dev->sriov.alias_guid.ports_guid[port_index]. |
166 | all_rec_per_port[record_num]. |
167 | guids_retry_schedule[index] = 0; |
168 | do_work = 1; |
169 | unlock: |
170 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags); |
171 | |
172 | if (do_work) |
173 | mlx4_ib_init_alias_guid_work(dev, port: port_index); |
174 | } |
175 | |
176 | /* |
177 | * Whenever new GUID is set/unset (guid table change) create event and |
178 | * notify the relevant slave (master also should be notified). |
179 | * If the GUID value is not as we have in the cache the slave will not be |
180 | * updated; in this case it waits for the smp_snoop or the port management |
181 | * event to call the function and to update the slave. |
182 | * block_number - the index of the block (16 blocks available) |
183 | * port_number - 1 or 2 |
184 | */ |
185 | void mlx4_ib_notify_slaves_on_guid_change(struct mlx4_ib_dev *dev, |
186 | int block_num, u32 port_num, |
187 | u8 *p_data) |
188 | { |
189 | int i; |
190 | u64 guid_indexes; |
191 | int slave_id, slave_port; |
192 | enum slave_port_state new_state; |
193 | enum slave_port_state prev_state; |
194 | __be64 tmp_cur_ag, form_cache_ag; |
195 | enum slave_port_gen_event gen_event; |
196 | struct mlx4_sriov_alias_guid_info_rec_det *rec; |
197 | unsigned long flags; |
198 | __be64 required_value; |
199 | |
200 | if (!mlx4_is_master(dev: dev->dev)) |
201 | return; |
202 | |
203 | rec = &dev->sriov.alias_guid.ports_guid[port_num - 1]. |
204 | all_rec_per_port[block_num]; |
205 | guid_indexes = be64_to_cpu((__force __be64) dev->sriov.alias_guid. |
206 | ports_guid[port_num - 1]. |
207 | all_rec_per_port[block_num].guid_indexes); |
208 | pr_debug("port: %u, guid_indexes: 0x%llx\n" , port_num, guid_indexes); |
209 | |
210 | /*calculate the slaves and notify them*/ |
211 | for (i = 0; i < NUM_ALIAS_GUID_IN_REC; i++) { |
212 | /* the location of the specific index runs from bits 4..11 */ |
213 | if (!(test_bit(i + 4, (unsigned long *)&guid_indexes))) |
214 | continue; |
215 | |
216 | slave_id = (block_num * NUM_ALIAS_GUID_IN_REC) + i ; |
217 | if (slave_id >= dev->dev->persist->num_vfs + 1) |
218 | return; |
219 | |
220 | slave_port = mlx4_phys_to_slave_port(dev: dev->dev, slave: slave_id, port: port_num); |
221 | if (slave_port < 0) /* this port isn't available for the VF */ |
222 | continue; |
223 | |
224 | tmp_cur_ag = *(__be64 *)&p_data[i * GUID_REC_SIZE]; |
225 | form_cache_ag = get_cached_alias_guid(dev, port: port_num, |
226 | index: (NUM_ALIAS_GUID_IN_REC * block_num) + i); |
227 | /* |
228 | * Check if guid is not the same as in the cache, |
229 | * If it is different, wait for the snoop_smp or the port mgmt |
230 | * change event to update the slave on its port state change |
231 | */ |
232 | if (tmp_cur_ag != form_cache_ag) |
233 | continue; |
234 | |
235 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags); |
236 | required_value = *(__be64 *)&rec->all_recs[i * GUID_REC_SIZE]; |
237 | |
238 | if (required_value == cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL)) |
239 | required_value = 0; |
240 | |
241 | if (tmp_cur_ag == required_value) { |
242 | rec->guid_indexes = rec->guid_indexes & |
243 | ~mlx4_ib_get_aguid_comp_mask_from_ix(index: i); |
244 | } else { |
245 | /* may notify port down if value is 0 */ |
246 | if (tmp_cur_ag != MLX4_NOT_SET_GUID) { |
247 | spin_unlock_irqrestore(lock: &dev->sriov. |
248 | alias_guid.ag_work_lock, flags); |
249 | continue; |
250 | } |
251 | } |
252 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, |
253 | flags); |
254 | mlx4_gen_guid_change_eqe(dev: dev->dev, slave: slave_id, port: port_num); |
255 | /*2 cases: Valid GUID, and Invalid Guid*/ |
256 | |
257 | if (tmp_cur_ag != MLX4_NOT_SET_GUID) { /*valid GUID*/ |
258 | prev_state = mlx4_get_slave_port_state(dev: dev->dev, slave: slave_id, port: port_num); |
259 | new_state = set_and_calc_slave_port_state(dev: dev->dev, slave: slave_id, port: port_num, |
260 | event: MLX4_PORT_STATE_IB_PORT_STATE_EVENT_GID_VALID, |
261 | gen_event: &gen_event); |
262 | pr_debug("slave: %d, port: %u prev_port_state: %d," |
263 | " new_port_state: %d, gen_event: %d\n" , |
264 | slave_id, port_num, prev_state, new_state, gen_event); |
265 | if (gen_event == SLAVE_PORT_GEN_EVENT_UP) { |
266 | pr_debug("sending PORT_UP event to slave: %d, port: %u\n" , |
267 | slave_id, port_num); |
268 | mlx4_gen_port_state_change_eqe(dev: dev->dev, slave: slave_id, |
269 | port: port_num, port_subtype_change: MLX4_PORT_CHANGE_SUBTYPE_ACTIVE); |
270 | } |
271 | } else { /* request to invalidate GUID */ |
272 | set_and_calc_slave_port_state(dev: dev->dev, slave: slave_id, port: port_num, |
273 | event: MLX4_PORT_STATE_IB_EVENT_GID_INVALID, |
274 | gen_event: &gen_event); |
275 | if (gen_event == SLAVE_PORT_GEN_EVENT_DOWN) { |
276 | pr_debug("sending PORT DOWN event to slave: %d, port: %u\n" , |
277 | slave_id, port_num); |
278 | mlx4_gen_port_state_change_eqe(dev: dev->dev, |
279 | slave: slave_id, |
280 | port: port_num, |
281 | port_subtype_change: MLX4_PORT_CHANGE_SUBTYPE_DOWN); |
282 | } |
283 | } |
284 | } |
285 | } |
286 | |
287 | static void aliasguid_query_handler(int status, |
288 | struct ib_sa_guidinfo_rec *guid_rec, |
289 | void *context) |
290 | { |
291 | struct mlx4_ib_dev *dev; |
292 | struct mlx4_alias_guid_work_context *cb_ctx = context; |
293 | u8 port_index ; |
294 | int i; |
295 | struct mlx4_sriov_alias_guid_info_rec_det *rec; |
296 | unsigned long flags, flags1; |
297 | ib_sa_comp_mask declined_guid_indexes = 0; |
298 | ib_sa_comp_mask applied_guid_indexes = 0; |
299 | unsigned int resched_delay_sec = 0; |
300 | |
301 | if (!context) |
302 | return; |
303 | |
304 | dev = cb_ctx->dev; |
305 | port_index = cb_ctx->port - 1; |
306 | rec = &dev->sriov.alias_guid.ports_guid[port_index]. |
307 | all_rec_per_port[cb_ctx->block_num]; |
308 | |
309 | if (status) { |
310 | pr_debug("(port: %d) failed: status = %d\n" , |
311 | cb_ctx->port, status); |
312 | rec->time_to_run = ktime_get_boottime_ns() + 1 * NSEC_PER_SEC; |
313 | goto out; |
314 | } |
315 | |
316 | if (guid_rec->block_num != cb_ctx->block_num) { |
317 | pr_err("block num mismatch: %d != %d\n" , |
318 | cb_ctx->block_num, guid_rec->block_num); |
319 | goto out; |
320 | } |
321 | |
322 | pr_debug("lid/port: %d/%d, block_num: %d\n" , |
323 | be16_to_cpu(guid_rec->lid), cb_ctx->port, |
324 | guid_rec->block_num); |
325 | |
326 | rec = &dev->sriov.alias_guid.ports_guid[port_index]. |
327 | all_rec_per_port[guid_rec->block_num]; |
328 | |
329 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags); |
330 | for (i = 0 ; i < NUM_ALIAS_GUID_IN_REC; i++) { |
331 | __be64 sm_response, required_val; |
332 | |
333 | if (!(cb_ctx->guid_indexes & |
334 | mlx4_ib_get_aguid_comp_mask_from_ix(index: i))) |
335 | continue; |
336 | sm_response = *(__be64 *)&guid_rec->guid_info_list |
337 | [i * GUID_REC_SIZE]; |
338 | required_val = *(__be64 *)&rec->all_recs[i * GUID_REC_SIZE]; |
339 | if (cb_ctx->method == MLX4_GUID_INFO_RECORD_DELETE) { |
340 | if (required_val == |
341 | cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL)) |
342 | goto next_entry; |
343 | |
344 | /* A new value was set till we got the response */ |
345 | pr_debug("need to set new value %llx, record num %d, block_num:%d\n" , |
346 | be64_to_cpu(required_val), |
347 | i, guid_rec->block_num); |
348 | goto entry_declined; |
349 | } |
350 | |
351 | /* check if the SM didn't assign one of the records. |
352 | * if it didn't, re-ask for. |
353 | */ |
354 | if (sm_response == MLX4_NOT_SET_GUID) { |
355 | if (rec->guids_retry_schedule[i] == 0) |
356 | mlx4_ib_warn(&dev->ib_dev, |
357 | "%s:Record num %d in block_num: %d was declined by SM\n" , |
358 | __func__, i, |
359 | guid_rec->block_num); |
360 | goto entry_declined; |
361 | } else { |
362 | /* properly assigned record. */ |
363 | /* We save the GUID we just got from the SM in the |
364 | * admin_guid in order to be persistent, and in the |
365 | * request from the sm the process will ask for the same GUID */ |
366 | if (required_val && |
367 | sm_response != required_val) { |
368 | /* Warn only on first retry */ |
369 | if (rec->guids_retry_schedule[i] == 0) |
370 | mlx4_ib_warn(&dev->ib_dev, "%s: Failed to set" |
371 | " admin guid after SysAdmin " |
372 | "configuration. " |
373 | "Record num %d in block_num:%d " |
374 | "was declined by SM, " |
375 | "new val(0x%llx) was kept, SM returned (0x%llx)\n" , |
376 | __func__, i, |
377 | guid_rec->block_num, |
378 | be64_to_cpu(required_val), |
379 | be64_to_cpu(sm_response)); |
380 | goto entry_declined; |
381 | } else { |
382 | *(__be64 *)&rec->all_recs[i * GUID_REC_SIZE] = |
383 | sm_response; |
384 | if (required_val == 0) |
385 | mlx4_set_admin_guid(dev: dev->dev, |
386 | guid: sm_response, |
387 | entry: (guid_rec->block_num |
388 | * NUM_ALIAS_GUID_IN_REC) + i, |
389 | port: cb_ctx->port); |
390 | goto next_entry; |
391 | } |
392 | } |
393 | entry_declined: |
394 | declined_guid_indexes |= mlx4_ib_get_aguid_comp_mask_from_ix(index: i); |
395 | rec->guids_retry_schedule[i] = |
396 | (rec->guids_retry_schedule[i] == 0) ? 1 : |
397 | min((unsigned int)60, |
398 | rec->guids_retry_schedule[i] * 2); |
399 | /* using the minimum value among all entries in that record */ |
400 | resched_delay_sec = (resched_delay_sec == 0) ? |
401 | rec->guids_retry_schedule[i] : |
402 | min(resched_delay_sec, |
403 | rec->guids_retry_schedule[i]); |
404 | continue; |
405 | |
406 | next_entry: |
407 | rec->guids_retry_schedule[i] = 0; |
408 | } |
409 | |
410 | applied_guid_indexes = cb_ctx->guid_indexes & ~declined_guid_indexes; |
411 | if (declined_guid_indexes || |
412 | rec->guid_indexes & ~(applied_guid_indexes)) { |
413 | pr_debug("record=%d wasn't fully set, guid_indexes=0x%llx applied_indexes=0x%llx, declined_indexes=0x%llx\n" , |
414 | guid_rec->block_num, |
415 | be64_to_cpu((__force __be64)rec->guid_indexes), |
416 | be64_to_cpu((__force __be64)applied_guid_indexes), |
417 | be64_to_cpu((__force __be64)declined_guid_indexes)); |
418 | rec->time_to_run = ktime_get_boottime_ns() + |
419 | resched_delay_sec * NSEC_PER_SEC; |
420 | } else { |
421 | rec->status = MLX4_GUID_INFO_STATUS_SET; |
422 | } |
423 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags); |
424 | /* |
425 | The func is call here to close the cases when the |
426 | sm doesn't send smp, so in the sa response the driver |
427 | notifies the slave. |
428 | */ |
429 | mlx4_ib_notify_slaves_on_guid_change(dev, block_num: guid_rec->block_num, |
430 | port_num: cb_ctx->port, |
431 | p_data: guid_rec->guid_info_list); |
432 | out: |
433 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); |
434 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); |
435 | if (!dev->sriov.is_going_down) { |
436 | get_low_record_time_index(dev, port: port_index, resched_delay_sec: &resched_delay_sec); |
437 | queue_delayed_work(wq: dev->sriov.alias_guid.ports_guid[port_index].wq, |
438 | dwork: &dev->sriov.alias_guid.ports_guid[port_index]. |
439 | alias_guid_work, |
440 | delay: msecs_to_jiffies(m: resched_delay_sec * 1000)); |
441 | } |
442 | if (cb_ctx->sa_query) { |
443 | list_del(entry: &cb_ctx->list); |
444 | kfree(objp: cb_ctx); |
445 | } else |
446 | complete(&cb_ctx->done); |
447 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags: flags1); |
448 | spin_unlock_irqrestore(lock: &dev->sriov.going_down_lock, flags); |
449 | } |
450 | |
451 | static void invalidate_guid_record(struct mlx4_ib_dev *dev, u8 port, int index) |
452 | { |
453 | int i; |
454 | u64 cur_admin_val; |
455 | ib_sa_comp_mask comp_mask = 0; |
456 | |
457 | dev->sriov.alias_guid.ports_guid[port - 1].all_rec_per_port[index].status |
458 | = MLX4_GUID_INFO_STATUS_SET; |
459 | |
460 | /* calculate the comp_mask for that record.*/ |
461 | for (i = 0; i < NUM_ALIAS_GUID_IN_REC; i++) { |
462 | cur_admin_val = |
463 | *(u64 *)&dev->sriov.alias_guid.ports_guid[port - 1]. |
464 | all_rec_per_port[index].all_recs[GUID_REC_SIZE * i]; |
465 | /* |
466 | check the admin value: if it's for delete (~00LL) or |
467 | it is the first guid of the first record (hw guid) or |
468 | the records is not in ownership of the sysadmin and the sm doesn't |
469 | need to assign GUIDs, then don't put it up for assignment. |
470 | */ |
471 | if (MLX4_GUID_FOR_DELETE_VAL == cur_admin_val || |
472 | (!index && !i)) |
473 | continue; |
474 | comp_mask |= mlx4_ib_get_aguid_comp_mask_from_ix(index: i); |
475 | } |
476 | dev->sriov.alias_guid.ports_guid[port - 1]. |
477 | all_rec_per_port[index].guid_indexes |= comp_mask; |
478 | if (dev->sriov.alias_guid.ports_guid[port - 1]. |
479 | all_rec_per_port[index].guid_indexes) |
480 | dev->sriov.alias_guid.ports_guid[port - 1]. |
481 | all_rec_per_port[index].status = MLX4_GUID_INFO_STATUS_IDLE; |
482 | |
483 | } |
484 | |
485 | static int set_guid_rec(struct ib_device *ibdev, |
486 | struct mlx4_next_alias_guid_work *rec) |
487 | { |
488 | int err; |
489 | struct mlx4_ib_dev *dev = to_mdev(ibdev); |
490 | struct ib_sa_guidinfo_rec guid_info_rec; |
491 | ib_sa_comp_mask comp_mask; |
492 | struct ib_port_attr attr; |
493 | struct mlx4_alias_guid_work_context *callback_context; |
494 | unsigned long resched_delay, flags, flags1; |
495 | u8 port = rec->port + 1; |
496 | int index = rec->block_num; |
497 | struct mlx4_sriov_alias_guid_info_rec_det *rec_det = &rec->rec_det; |
498 | struct list_head *head = |
499 | &dev->sriov.alias_guid.ports_guid[port - 1].cb_list; |
500 | |
501 | memset(&attr, 0, sizeof(attr)); |
502 | err = __mlx4_ib_query_port(ibdev, port, props: &attr, netw_view: 1); |
503 | if (err) { |
504 | pr_debug("mlx4_ib_query_port failed (err: %d), port: %d\n" , |
505 | err, port); |
506 | return err; |
507 | } |
508 | /*check the port was configured by the sm, otherwise no need to send */ |
509 | if (attr.state != IB_PORT_ACTIVE) { |
510 | pr_debug("port %d not active...rescheduling\n" , port); |
511 | resched_delay = 5 * HZ; |
512 | err = -EAGAIN; |
513 | goto new_schedule; |
514 | } |
515 | |
516 | callback_context = kmalloc(size: sizeof *callback_context, GFP_KERNEL); |
517 | if (!callback_context) { |
518 | err = -ENOMEM; |
519 | resched_delay = HZ * 5; |
520 | goto new_schedule; |
521 | } |
522 | callback_context->port = port; |
523 | callback_context->dev = dev; |
524 | callback_context->block_num = index; |
525 | callback_context->guid_indexes = rec_det->guid_indexes; |
526 | callback_context->method = rec->method; |
527 | |
528 | memset(&guid_info_rec, 0, sizeof (struct ib_sa_guidinfo_rec)); |
529 | |
530 | guid_info_rec.lid = ib_lid_be16(lid: attr.lid); |
531 | guid_info_rec.block_num = index; |
532 | |
533 | memcpy(guid_info_rec.guid_info_list, rec_det->all_recs, |
534 | GUID_REC_SIZE * NUM_ALIAS_GUID_IN_REC); |
535 | comp_mask = IB_SA_GUIDINFO_REC_LID | IB_SA_GUIDINFO_REC_BLOCK_NUM | |
536 | rec_det->guid_indexes; |
537 | |
538 | init_completion(x: &callback_context->done); |
539 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); |
540 | list_add_tail(new: &callback_context->list, head); |
541 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags: flags1); |
542 | |
543 | callback_context->query_id = |
544 | ib_sa_guid_info_rec_query(client: dev->sriov.alias_guid.sa_client, |
545 | device: ibdev, port_num: port, rec: &guid_info_rec, |
546 | comp_mask, method: rec->method, timeout_ms: 1000, |
547 | GFP_KERNEL, callback: aliasguid_query_handler, |
548 | context: callback_context, |
549 | sa_query: &callback_context->sa_query); |
550 | if (callback_context->query_id < 0) { |
551 | pr_debug("ib_sa_guid_info_rec_query failed, query_id: " |
552 | "%d. will reschedule to the next 1 sec.\n" , |
553 | callback_context->query_id); |
554 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); |
555 | list_del(entry: &callback_context->list); |
556 | kfree(objp: callback_context); |
557 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags: flags1); |
558 | resched_delay = 1 * HZ; |
559 | err = -EAGAIN; |
560 | goto new_schedule; |
561 | } |
562 | err = 0; |
563 | goto out; |
564 | |
565 | new_schedule: |
566 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); |
567 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); |
568 | invalidate_guid_record(dev, port, index); |
569 | if (!dev->sriov.is_going_down) { |
570 | queue_delayed_work(wq: dev->sriov.alias_guid.ports_guid[port - 1].wq, |
571 | dwork: &dev->sriov.alias_guid.ports_guid[port - 1].alias_guid_work, |
572 | delay: resched_delay); |
573 | } |
574 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags: flags1); |
575 | spin_unlock_irqrestore(lock: &dev->sriov.going_down_lock, flags); |
576 | |
577 | out: |
578 | return err; |
579 | } |
580 | |
581 | static void mlx4_ib_guid_port_init(struct mlx4_ib_dev *dev, int port) |
582 | { |
583 | int j, k, entry; |
584 | __be64 guid; |
585 | |
586 | /*Check if the SM doesn't need to assign the GUIDs*/ |
587 | for (j = 0; j < NUM_ALIAS_GUID_REC_IN_PORT; j++) { |
588 | for (k = 0; k < NUM_ALIAS_GUID_IN_REC; k++) { |
589 | entry = j * NUM_ALIAS_GUID_IN_REC + k; |
590 | /* no request for the 0 entry (hw guid) */ |
591 | if (!entry || entry > dev->dev->persist->num_vfs || |
592 | !mlx4_is_slave_active(dev: dev->dev, slave: entry)) |
593 | continue; |
594 | guid = mlx4_get_admin_guid(dev: dev->dev, entry, port); |
595 | *(__be64 *)&dev->sriov.alias_guid.ports_guid[port - 1]. |
596 | all_rec_per_port[j].all_recs |
597 | [GUID_REC_SIZE * k] = guid; |
598 | pr_debug("guid was set, entry=%d, val=0x%llx, port=%d\n" , |
599 | entry, |
600 | be64_to_cpu(guid), |
601 | port); |
602 | } |
603 | } |
604 | } |
605 | void mlx4_ib_invalidate_all_guid_record(struct mlx4_ib_dev *dev, int port) |
606 | { |
607 | int i; |
608 | unsigned long flags, flags1; |
609 | |
610 | pr_debug("port %d\n" , port); |
611 | |
612 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); |
613 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); |
614 | |
615 | if (dev->sriov.alias_guid.ports_guid[port - 1].state_flags & |
616 | GUID_STATE_NEED_PORT_INIT) { |
617 | mlx4_ib_guid_port_init(dev, port); |
618 | dev->sriov.alias_guid.ports_guid[port - 1].state_flags &= |
619 | (~GUID_STATE_NEED_PORT_INIT); |
620 | } |
621 | for (i = 0; i < NUM_ALIAS_GUID_REC_IN_PORT; i++) |
622 | invalidate_guid_record(dev, port, index: i); |
623 | |
624 | if (mlx4_is_master(dev: dev->dev) && !dev->sriov.is_going_down) { |
625 | /* |
626 | make sure no work waits in the queue, if the work is already |
627 | queued(not on the timer) the cancel will fail. That is not a problem |
628 | because we just want the work started. |
629 | */ |
630 | cancel_delayed_work(dwork: &dev->sriov.alias_guid. |
631 | ports_guid[port - 1].alias_guid_work); |
632 | queue_delayed_work(wq: dev->sriov.alias_guid.ports_guid[port - 1].wq, |
633 | dwork: &dev->sriov.alias_guid.ports_guid[port - 1].alias_guid_work, |
634 | delay: 0); |
635 | } |
636 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags: flags1); |
637 | spin_unlock_irqrestore(lock: &dev->sriov.going_down_lock, flags); |
638 | } |
639 | |
640 | static void set_required_record(struct mlx4_ib_dev *dev, u8 port, |
641 | struct mlx4_next_alias_guid_work *next_rec, |
642 | int record_index) |
643 | { |
644 | int i; |
645 | int lowset_time_entry = -1; |
646 | int lowest_time = 0; |
647 | ib_sa_comp_mask delete_guid_indexes = 0; |
648 | ib_sa_comp_mask set_guid_indexes = 0; |
649 | struct mlx4_sriov_alias_guid_info_rec_det *rec = |
650 | &dev->sriov.alias_guid.ports_guid[port]. |
651 | all_rec_per_port[record_index]; |
652 | |
653 | for (i = 0; i < NUM_ALIAS_GUID_IN_REC; i++) { |
654 | if (!(rec->guid_indexes & |
655 | mlx4_ib_get_aguid_comp_mask_from_ix(index: i))) |
656 | continue; |
657 | |
658 | if (*(__be64 *)&rec->all_recs[i * GUID_REC_SIZE] == |
659 | cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL)) |
660 | delete_guid_indexes |= |
661 | mlx4_ib_get_aguid_comp_mask_from_ix(index: i); |
662 | else |
663 | set_guid_indexes |= |
664 | mlx4_ib_get_aguid_comp_mask_from_ix(index: i); |
665 | |
666 | if (lowset_time_entry == -1 || rec->guids_retry_schedule[i] <= |
667 | lowest_time) { |
668 | lowset_time_entry = i; |
669 | lowest_time = rec->guids_retry_schedule[i]; |
670 | } |
671 | } |
672 | |
673 | memcpy(&next_rec->rec_det, rec, sizeof(*rec)); |
674 | next_rec->port = port; |
675 | next_rec->block_num = record_index; |
676 | |
677 | if (*(__be64 *)&rec->all_recs[lowset_time_entry * GUID_REC_SIZE] == |
678 | cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL)) { |
679 | next_rec->rec_det.guid_indexes = delete_guid_indexes; |
680 | next_rec->method = MLX4_GUID_INFO_RECORD_DELETE; |
681 | } else { |
682 | next_rec->rec_det.guid_indexes = set_guid_indexes; |
683 | next_rec->method = MLX4_GUID_INFO_RECORD_SET; |
684 | } |
685 | } |
686 | |
687 | /* return index of record that should be updated based on lowest |
688 | * rescheduled time |
689 | */ |
690 | static int get_low_record_time_index(struct mlx4_ib_dev *dev, u8 port, |
691 | int *resched_delay_sec) |
692 | { |
693 | int record_index = -1; |
694 | u64 low_record_time = 0; |
695 | struct mlx4_sriov_alias_guid_info_rec_det rec; |
696 | int j; |
697 | |
698 | for (j = 0; j < NUM_ALIAS_GUID_REC_IN_PORT; j++) { |
699 | rec = dev->sriov.alias_guid.ports_guid[port]. |
700 | all_rec_per_port[j]; |
701 | if (rec.status == MLX4_GUID_INFO_STATUS_IDLE && |
702 | rec.guid_indexes) { |
703 | if (record_index == -1 || |
704 | rec.time_to_run < low_record_time) { |
705 | record_index = j; |
706 | low_record_time = rec.time_to_run; |
707 | } |
708 | } |
709 | } |
710 | if (resched_delay_sec) { |
711 | u64 curr_time = ktime_get_boottime_ns(); |
712 | |
713 | *resched_delay_sec = (low_record_time < curr_time) ? 0 : |
714 | div_u64(dividend: (low_record_time - curr_time), NSEC_PER_SEC); |
715 | } |
716 | |
717 | return record_index; |
718 | } |
719 | |
720 | /* The function returns the next record that was |
721 | * not configured (or failed to be configured) */ |
722 | static int get_next_record_to_update(struct mlx4_ib_dev *dev, u8 port, |
723 | struct mlx4_next_alias_guid_work *rec) |
724 | { |
725 | unsigned long flags; |
726 | int record_index; |
727 | int ret = 0; |
728 | |
729 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags); |
730 | record_index = get_low_record_time_index(dev, port, NULL); |
731 | |
732 | if (record_index < 0) { |
733 | ret = -ENOENT; |
734 | goto out; |
735 | } |
736 | |
737 | set_required_record(dev, port, next_rec: rec, record_index); |
738 | out: |
739 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags); |
740 | return ret; |
741 | } |
742 | |
743 | static void alias_guid_work(struct work_struct *work) |
744 | { |
745 | struct delayed_work *delay = to_delayed_work(work); |
746 | int ret = 0; |
747 | struct mlx4_next_alias_guid_work *rec; |
748 | struct mlx4_sriov_alias_guid_port_rec_det *sriov_alias_port = |
749 | container_of(delay, struct mlx4_sriov_alias_guid_port_rec_det, |
750 | alias_guid_work); |
751 | struct mlx4_sriov_alias_guid *sriov_alias_guid = sriov_alias_port->parent; |
752 | struct mlx4_ib_sriov *ib_sriov = container_of(sriov_alias_guid, |
753 | struct mlx4_ib_sriov, |
754 | alias_guid); |
755 | struct mlx4_ib_dev *dev = container_of(ib_sriov, struct mlx4_ib_dev, sriov); |
756 | |
757 | rec = kzalloc(size: sizeof *rec, GFP_KERNEL); |
758 | if (!rec) |
759 | return; |
760 | |
761 | pr_debug("starting [port: %d]...\n" , sriov_alias_port->port + 1); |
762 | ret = get_next_record_to_update(dev, port: sriov_alias_port->port, rec); |
763 | if (ret) { |
764 | pr_debug("No more records to update.\n" ); |
765 | goto out; |
766 | } |
767 | |
768 | set_guid_rec(ibdev: &dev->ib_dev, rec); |
769 | out: |
770 | kfree(objp: rec); |
771 | } |
772 | |
773 | |
774 | void mlx4_ib_init_alias_guid_work(struct mlx4_ib_dev *dev, int port) |
775 | { |
776 | unsigned long flags, flags1; |
777 | |
778 | if (!mlx4_is_master(dev: dev->dev)) |
779 | return; |
780 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); |
781 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); |
782 | if (!dev->sriov.is_going_down) { |
783 | /* If there is pending one should cancel then run, otherwise |
784 | * won't run till previous one is ended as same work |
785 | * struct is used. |
786 | */ |
787 | cancel_delayed_work(dwork: &dev->sriov.alias_guid.ports_guid[port]. |
788 | alias_guid_work); |
789 | queue_delayed_work(wq: dev->sriov.alias_guid.ports_guid[port].wq, |
790 | dwork: &dev->sriov.alias_guid.ports_guid[port].alias_guid_work, delay: 0); |
791 | } |
792 | spin_unlock_irqrestore(lock: &dev->sriov.alias_guid.ag_work_lock, flags: flags1); |
793 | spin_unlock_irqrestore(lock: &dev->sriov.going_down_lock, flags); |
794 | } |
795 | |
796 | void mlx4_ib_destroy_alias_guid_service(struct mlx4_ib_dev *dev) |
797 | { |
798 | int i; |
799 | struct mlx4_ib_sriov *sriov = &dev->sriov; |
800 | struct mlx4_alias_guid_work_context *cb_ctx; |
801 | struct mlx4_sriov_alias_guid_port_rec_det *det; |
802 | struct ib_sa_query *sa_query; |
803 | unsigned long flags; |
804 | |
805 | for (i = 0 ; i < dev->num_ports; i++) { |
806 | det = &sriov->alias_guid.ports_guid[i]; |
807 | cancel_delayed_work_sync(dwork: &det->alias_guid_work); |
808 | spin_lock_irqsave(&sriov->alias_guid.ag_work_lock, flags); |
809 | while (!list_empty(head: &det->cb_list)) { |
810 | cb_ctx = list_entry(det->cb_list.next, |
811 | struct mlx4_alias_guid_work_context, |
812 | list); |
813 | sa_query = cb_ctx->sa_query; |
814 | cb_ctx->sa_query = NULL; |
815 | list_del(entry: &cb_ctx->list); |
816 | spin_unlock_irqrestore(lock: &sriov->alias_guid.ag_work_lock, flags); |
817 | ib_sa_cancel_query(id: cb_ctx->query_id, query: sa_query); |
818 | wait_for_completion(&cb_ctx->done); |
819 | kfree(objp: cb_ctx); |
820 | spin_lock_irqsave(&sriov->alias_guid.ag_work_lock, flags); |
821 | } |
822 | spin_unlock_irqrestore(lock: &sriov->alias_guid.ag_work_lock, flags); |
823 | } |
824 | for (i = 0 ; i < dev->num_ports; i++) |
825 | destroy_workqueue(wq: dev->sriov.alias_guid.ports_guid[i].wq); |
826 | ib_sa_unregister_client(client: dev->sriov.alias_guid.sa_client); |
827 | kfree(objp: dev->sriov.alias_guid.sa_client); |
828 | } |
829 | |
830 | int mlx4_ib_init_alias_guid_service(struct mlx4_ib_dev *dev) |
831 | { |
832 | char alias_wq_name[15]; |
833 | int ret = 0; |
834 | int i, j; |
835 | union ib_gid gid; |
836 | |
837 | if (!mlx4_is_master(dev: dev->dev)) |
838 | return 0; |
839 | dev->sriov.alias_guid.sa_client = |
840 | kzalloc(size: sizeof *dev->sriov.alias_guid.sa_client, GFP_KERNEL); |
841 | if (!dev->sriov.alias_guid.sa_client) |
842 | return -ENOMEM; |
843 | |
844 | ib_sa_register_client(client: dev->sriov.alias_guid.sa_client); |
845 | |
846 | spin_lock_init(&dev->sriov.alias_guid.ag_work_lock); |
847 | |
848 | for (i = 1; i <= dev->num_ports; ++i) { |
849 | if (dev->ib_dev.ops.query_gid(&dev->ib_dev, i, 0, &gid)) { |
850 | ret = -EFAULT; |
851 | goto err_unregister; |
852 | } |
853 | } |
854 | |
855 | for (i = 0 ; i < dev->num_ports; i++) { |
856 | memset(&dev->sriov.alias_guid.ports_guid[i], 0, |
857 | sizeof (struct mlx4_sriov_alias_guid_port_rec_det)); |
858 | dev->sriov.alias_guid.ports_guid[i].state_flags |= |
859 | GUID_STATE_NEED_PORT_INIT; |
860 | for (j = 0; j < NUM_ALIAS_GUID_REC_IN_PORT; j++) { |
861 | /* mark each val as it was deleted */ |
862 | memset(dev->sriov.alias_guid.ports_guid[i]. |
863 | all_rec_per_port[j].all_recs, 0xFF, |
864 | sizeof(dev->sriov.alias_guid.ports_guid[i]. |
865 | all_rec_per_port[j].all_recs)); |
866 | } |
867 | INIT_LIST_HEAD(list: &dev->sriov.alias_guid.ports_guid[i].cb_list); |
868 | /*prepare the records, set them to be allocated by sm*/ |
869 | if (mlx4_ib_sm_guid_assign) |
870 | for (j = 1; j < NUM_ALIAS_GUID_PER_PORT; j++) |
871 | mlx4_set_admin_guid(dev: dev->dev, guid: 0, entry: j, port: i + 1); |
872 | for (j = 0 ; j < NUM_ALIAS_GUID_REC_IN_PORT; j++) |
873 | invalidate_guid_record(dev, port: i + 1, index: j); |
874 | |
875 | dev->sriov.alias_guid.ports_guid[i].parent = &dev->sriov.alias_guid; |
876 | dev->sriov.alias_guid.ports_guid[i].port = i; |
877 | |
878 | snprintf(buf: alias_wq_name, size: sizeof alias_wq_name, fmt: "alias_guid%d" , i); |
879 | dev->sriov.alias_guid.ports_guid[i].wq = |
880 | alloc_ordered_workqueue(alias_wq_name, WQ_MEM_RECLAIM); |
881 | if (!dev->sriov.alias_guid.ports_guid[i].wq) { |
882 | ret = -ENOMEM; |
883 | goto err_thread; |
884 | } |
885 | INIT_DELAYED_WORK(&dev->sriov.alias_guid.ports_guid[i].alias_guid_work, |
886 | alias_guid_work); |
887 | } |
888 | return 0; |
889 | |
890 | err_thread: |
891 | for (--i; i >= 0; i--) { |
892 | destroy_workqueue(wq: dev->sriov.alias_guid.ports_guid[i].wq); |
893 | dev->sriov.alias_guid.ports_guid[i].wq = NULL; |
894 | } |
895 | |
896 | err_unregister: |
897 | ib_sa_unregister_client(client: dev->sriov.alias_guid.sa_client); |
898 | kfree(objp: dev->sriov.alias_guid.sa_client); |
899 | dev->sriov.alias_guid.sa_client = NULL; |
900 | pr_err("init_alias_guid_service: Failed. (ret:%d)\n" , ret); |
901 | return ret; |
902 | } |
903 | |