diff options
Diffstat (limited to 'drivers/infiniband/hw/mlx4/alias_GUID.c')
-rw-r--r-- | drivers/infiniband/hw/mlx4/alias_GUID.c | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/drivers/infiniband/hw/mlx4/alias_GUID.c b/drivers/infiniband/hw/mlx4/alias_GUID.c new file mode 100644 index 000000000000..d2fb38d43571 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/alias_GUID.c | |||
@@ -0,0 +1,688 @@ | |||
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/module.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/errno.h> | ||
44 | #include <rdma/ib_user_verbs.h> | ||
45 | #include <linux/delay.h> | ||
46 | #include "mlx4_ib.h" | ||
47 | |||
48 | /* | ||
49 | The driver keeps the current state of all guids, as they are in the HW. | ||
50 | Whenever we receive an smp mad GUIDInfo record, the data will be cached. | ||
51 | */ | ||
52 | |||
53 | struct mlx4_alias_guid_work_context { | ||
54 | u8 port; | ||
55 | struct mlx4_ib_dev *dev ; | ||
56 | struct ib_sa_query *sa_query; | ||
57 | struct completion done; | ||
58 | int query_id; | ||
59 | struct list_head list; | ||
60 | int block_num; | ||
61 | }; | ||
62 | |||
63 | struct mlx4_next_alias_guid_work { | ||
64 | u8 port; | ||
65 | u8 block_num; | ||
66 | struct mlx4_sriov_alias_guid_info_rec_det rec_det; | ||
67 | }; | ||
68 | |||
69 | |||
70 | void mlx4_ib_update_cache_on_guid_change(struct mlx4_ib_dev *dev, int block_num, | ||
71 | u8 port_num, u8 *p_data) | ||
72 | { | ||
73 | int i; | ||
74 | u64 guid_indexes; | ||
75 | int slave_id; | ||
76 | int port_index = port_num - 1; | ||
77 | |||
78 | if (!mlx4_is_master(dev->dev)) | ||
79 | return; | ||
80 | |||
81 | guid_indexes = be64_to_cpu((__force __be64) dev->sriov.alias_guid. | ||
82 | ports_guid[port_num - 1]. | ||
83 | all_rec_per_port[block_num].guid_indexes); | ||
84 | pr_debug("port: %d, guid_indexes: 0x%llx\n", port_num, guid_indexes); | ||
85 | |||
86 | for (i = 0; i < NUM_ALIAS_GUID_IN_REC; i++) { | ||
87 | /* The location of the specific index starts from bit number 4 | ||
88 | * until bit num 11 */ | ||
89 | if (test_bit(i + 4, (unsigned long *)&guid_indexes)) { | ||
90 | slave_id = (block_num * NUM_ALIAS_GUID_IN_REC) + i ; | ||
91 | if (slave_id >= dev->dev->num_slaves) { | ||
92 | pr_debug("The last slave: %d\n", slave_id); | ||
93 | return; | ||
94 | } | ||
95 | |||
96 | /* cache the guid: */ | ||
97 | memcpy(&dev->sriov.demux[port_index].guid_cache[slave_id], | ||
98 | &p_data[i * GUID_REC_SIZE], | ||
99 | GUID_REC_SIZE); | ||
100 | } else | ||
101 | pr_debug("Guid number: %d in block: %d" | ||
102 | " was not updated\n", i, block_num); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static __be64 get_cached_alias_guid(struct mlx4_ib_dev *dev, int port, int index) | ||
107 | { | ||
108 | if (index >= NUM_ALIAS_GUID_PER_PORT) { | ||
109 | pr_err("%s: ERROR: asked for index:%d\n", __func__, index); | ||
110 | return (__force __be64) ((u64) 0xFFFFFFFFFFFFFFFFUL); | ||
111 | } | ||
112 | return *(__be64 *)&dev->sriov.demux[port - 1].guid_cache[index]; | ||
113 | } | ||
114 | |||
115 | |||
116 | ib_sa_comp_mask mlx4_ib_get_aguid_comp_mask_from_ix(int index) | ||
117 | { | ||
118 | return IB_SA_COMP_MASK(4 + index); | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Whenever new GUID is set/unset (guid table change) create event and | ||
123 | * notify the relevant slave (master also should be notified). | ||
124 | * If the GUID value is not as we have in the cache the slave will not be | ||
125 | * updated; in this case it waits for the smp_snoop or the port management | ||
126 | * event to call the function and to update the slave. | ||
127 | * block_number - the index of the block (16 blocks available) | ||
128 | * port_number - 1 or 2 | ||
129 | */ | ||
130 | void mlx4_ib_notify_slaves_on_guid_change(struct mlx4_ib_dev *dev, | ||
131 | int block_num, u8 port_num, | ||
132 | u8 *p_data) | ||
133 | { | ||
134 | int i; | ||
135 | u64 guid_indexes; | ||
136 | int slave_id; | ||
137 | enum slave_port_state new_state; | ||
138 | enum slave_port_state prev_state; | ||
139 | __be64 tmp_cur_ag, form_cache_ag; | ||
140 | enum slave_port_gen_event gen_event; | ||
141 | |||
142 | if (!mlx4_is_master(dev->dev)) | ||
143 | return; | ||
144 | |||
145 | guid_indexes = be64_to_cpu((__force __be64) dev->sriov.alias_guid. | ||
146 | ports_guid[port_num - 1]. | ||
147 | all_rec_per_port[block_num].guid_indexes); | ||
148 | pr_debug("port: %d, guid_indexes: 0x%llx\n", port_num, guid_indexes); | ||
149 | |||
150 | /*calculate the slaves and notify them*/ | ||
151 | for (i = 0; i < NUM_ALIAS_GUID_IN_REC; i++) { | ||
152 | /* the location of the specific index runs from bits 4..11 */ | ||
153 | if (!(test_bit(i + 4, (unsigned long *)&guid_indexes))) | ||
154 | continue; | ||
155 | |||
156 | slave_id = (block_num * NUM_ALIAS_GUID_IN_REC) + i ; | ||
157 | if (slave_id >= dev->dev->num_slaves) | ||
158 | return; | ||
159 | tmp_cur_ag = *(__be64 *)&p_data[i * GUID_REC_SIZE]; | ||
160 | form_cache_ag = get_cached_alias_guid(dev, port_num, | ||
161 | (NUM_ALIAS_GUID_IN_REC * block_num) + i); | ||
162 | /* | ||
163 | * Check if guid is not the same as in the cache, | ||
164 | * If it is different, wait for the snoop_smp or the port mgmt | ||
165 | * change event to update the slave on its port state change | ||
166 | */ | ||
167 | if (tmp_cur_ag != form_cache_ag) | ||
168 | continue; | ||
169 | mlx4_gen_guid_change_eqe(dev->dev, slave_id, port_num); | ||
170 | |||
171 | /*2 cases: Valid GUID, and Invalid Guid*/ | ||
172 | |||
173 | if (tmp_cur_ag != MLX4_NOT_SET_GUID) { /*valid GUID*/ | ||
174 | prev_state = mlx4_get_slave_port_state(dev->dev, slave_id, port_num); | ||
175 | new_state = set_and_calc_slave_port_state(dev->dev, slave_id, port_num, | ||
176 | MLX4_PORT_STATE_IB_PORT_STATE_EVENT_GID_VALID, | ||
177 | &gen_event); | ||
178 | pr_debug("slave: %d, port: %d prev_port_state: %d," | ||
179 | " new_port_state: %d, gen_event: %d\n", | ||
180 | slave_id, port_num, prev_state, new_state, gen_event); | ||
181 | if (gen_event == SLAVE_PORT_GEN_EVENT_UP) { | ||
182 | pr_debug("sending PORT_UP event to slave: %d, port: %d\n", | ||
183 | slave_id, port_num); | ||
184 | mlx4_gen_port_state_change_eqe(dev->dev, slave_id, | ||
185 | port_num, MLX4_PORT_CHANGE_SUBTYPE_ACTIVE); | ||
186 | } | ||
187 | } else { /* request to invalidate GUID */ | ||
188 | set_and_calc_slave_port_state(dev->dev, slave_id, port_num, | ||
189 | MLX4_PORT_STATE_IB_EVENT_GID_INVALID, | ||
190 | &gen_event); | ||
191 | pr_debug("sending PORT DOWN event to slave: %d, port: %d\n", | ||
192 | slave_id, port_num); | ||
193 | mlx4_gen_port_state_change_eqe(dev->dev, slave_id, port_num, | ||
194 | MLX4_PORT_CHANGE_SUBTYPE_DOWN); | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static void aliasguid_query_handler(int status, | ||
200 | struct ib_sa_guidinfo_rec *guid_rec, | ||
201 | void *context) | ||
202 | { | ||
203 | struct mlx4_ib_dev *dev; | ||
204 | struct mlx4_alias_guid_work_context *cb_ctx = context; | ||
205 | u8 port_index ; | ||
206 | int i; | ||
207 | struct mlx4_sriov_alias_guid_info_rec_det *rec; | ||
208 | unsigned long flags, flags1; | ||
209 | |||
210 | if (!context) | ||
211 | return; | ||
212 | |||
213 | dev = cb_ctx->dev; | ||
214 | port_index = cb_ctx->port - 1; | ||
215 | rec = &dev->sriov.alias_guid.ports_guid[port_index]. | ||
216 | all_rec_per_port[cb_ctx->block_num]; | ||
217 | |||
218 | if (status) { | ||
219 | rec->status = MLX4_GUID_INFO_STATUS_IDLE; | ||
220 | pr_debug("(port: %d) failed: status = %d\n", | ||
221 | cb_ctx->port, status); | ||
222 | goto out; | ||
223 | } | ||
224 | |||
225 | if (guid_rec->block_num != cb_ctx->block_num) { | ||
226 | pr_err("block num mismatch: %d != %d\n", | ||
227 | cb_ctx->block_num, guid_rec->block_num); | ||
228 | goto out; | ||
229 | } | ||
230 | |||
231 | pr_debug("lid/port: %d/%d, block_num: %d\n", | ||
232 | be16_to_cpu(guid_rec->lid), cb_ctx->port, | ||
233 | guid_rec->block_num); | ||
234 | |||
235 | rec = &dev->sriov.alias_guid.ports_guid[port_index]. | ||
236 | all_rec_per_port[guid_rec->block_num]; | ||
237 | |||
238 | rec->status = MLX4_GUID_INFO_STATUS_SET; | ||
239 | rec->method = MLX4_GUID_INFO_RECORD_SET; | ||
240 | |||
241 | for (i = 0 ; i < NUM_ALIAS_GUID_IN_REC; i++) { | ||
242 | __be64 tmp_cur_ag; | ||
243 | tmp_cur_ag = *(__be64 *)&guid_rec->guid_info_list[i * GUID_REC_SIZE]; | ||
244 | /* check if the SM didn't assign one of the records. | ||
245 | * if it didn't, if it was not sysadmin request: | ||
246 | * ask the SM to give a new GUID, (instead of the driver request). | ||
247 | */ | ||
248 | if (tmp_cur_ag == MLX4_NOT_SET_GUID) { | ||
249 | mlx4_ib_warn(&dev->ib_dev, "%s:Record num %d in " | ||
250 | "block_num: %d was declined by SM, " | ||
251 | "ownership by %d (0 = driver, 1=sysAdmin," | ||
252 | " 2=None)\n", __func__, i, | ||
253 | guid_rec->block_num, rec->ownership); | ||
254 | if (rec->ownership == MLX4_GUID_DRIVER_ASSIGN) { | ||
255 | /* if it is driver assign, asks for new GUID from SM*/ | ||
256 | *(__be64 *)&rec->all_recs[i * GUID_REC_SIZE] = | ||
257 | MLX4_NOT_SET_GUID; | ||
258 | |||
259 | /* Mark the record as not assigned, and let it | ||
260 | * be sent again in the next work sched.*/ | ||
261 | rec->status = MLX4_GUID_INFO_STATUS_IDLE; | ||
262 | rec->guid_indexes |= mlx4_ib_get_aguid_comp_mask_from_ix(i); | ||
263 | } | ||
264 | } else { | ||
265 | /* properly assigned record. */ | ||
266 | /* We save the GUID we just got from the SM in the | ||
267 | * admin_guid in order to be persistent, and in the | ||
268 | * request from the sm the process will ask for the same GUID */ | ||
269 | if (rec->ownership == MLX4_GUID_SYSADMIN_ASSIGN && | ||
270 | tmp_cur_ag != *(__be64 *)&rec->all_recs[i * GUID_REC_SIZE]) { | ||
271 | /* the sysadmin assignment failed.*/ | ||
272 | mlx4_ib_warn(&dev->ib_dev, "%s: Failed to set" | ||
273 | " admin guid after SysAdmin " | ||
274 | "configuration. " | ||
275 | "Record num %d in block_num:%d " | ||
276 | "was declined by SM, " | ||
277 | "new val(0x%llx) was kept\n", | ||
278 | __func__, i, | ||
279 | guid_rec->block_num, | ||
280 | be64_to_cpu(*(__be64 *) & | ||
281 | rec->all_recs[i * GUID_REC_SIZE])); | ||
282 | } else { | ||
283 | memcpy(&rec->all_recs[i * GUID_REC_SIZE], | ||
284 | &guid_rec->guid_info_list[i * GUID_REC_SIZE], | ||
285 | GUID_REC_SIZE); | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | /* | ||
290 | The func is call here to close the cases when the | ||
291 | sm doesn't send smp, so in the sa response the driver | ||
292 | notifies the slave. | ||
293 | */ | ||
294 | mlx4_ib_notify_slaves_on_guid_change(dev, guid_rec->block_num, | ||
295 | cb_ctx->port, | ||
296 | guid_rec->guid_info_list); | ||
297 | out: | ||
298 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); | ||
299 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
300 | if (!dev->sriov.is_going_down) | ||
301 | queue_delayed_work(dev->sriov.alias_guid.ports_guid[port_index].wq, | ||
302 | &dev->sriov.alias_guid.ports_guid[port_index]. | ||
303 | alias_guid_work, 0); | ||
304 | if (cb_ctx->sa_query) { | ||
305 | list_del(&cb_ctx->list); | ||
306 | kfree(cb_ctx); | ||
307 | } else | ||
308 | complete(&cb_ctx->done); | ||
309 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
310 | spin_unlock_irqrestore(&dev->sriov.going_down_lock, flags); | ||
311 | } | ||
312 | |||
313 | static void invalidate_guid_record(struct mlx4_ib_dev *dev, u8 port, int index) | ||
314 | { | ||
315 | int i; | ||
316 | u64 cur_admin_val; | ||
317 | ib_sa_comp_mask comp_mask = 0; | ||
318 | |||
319 | dev->sriov.alias_guid.ports_guid[port - 1].all_rec_per_port[index].status | ||
320 | = MLX4_GUID_INFO_STATUS_IDLE; | ||
321 | dev->sriov.alias_guid.ports_guid[port - 1].all_rec_per_port[index].method | ||
322 | = MLX4_GUID_INFO_RECORD_SET; | ||
323 | |||
324 | /* calculate the comp_mask for that record.*/ | ||
325 | for (i = 0; i < NUM_ALIAS_GUID_IN_REC; i++) { | ||
326 | cur_admin_val = | ||
327 | *(u64 *)&dev->sriov.alias_guid.ports_guid[port - 1]. | ||
328 | all_rec_per_port[index].all_recs[GUID_REC_SIZE * i]; | ||
329 | /* | ||
330 | check the admin value: if it's for delete (~00LL) or | ||
331 | it is the first guid of the first record (hw guid) or | ||
332 | the records is not in ownership of the sysadmin and the sm doesn't | ||
333 | need to assign GUIDs, then don't put it up for assignment. | ||
334 | */ | ||
335 | if (MLX4_GUID_FOR_DELETE_VAL == cur_admin_val || | ||
336 | (!index && !i) || | ||
337 | MLX4_GUID_NONE_ASSIGN == dev->sriov.alias_guid. | ||
338 | ports_guid[port - 1].all_rec_per_port[index].ownership) | ||
339 | continue; | ||
340 | comp_mask |= mlx4_ib_get_aguid_comp_mask_from_ix(i); | ||
341 | } | ||
342 | dev->sriov.alias_guid.ports_guid[port - 1]. | ||
343 | all_rec_per_port[index].guid_indexes = comp_mask; | ||
344 | } | ||
345 | |||
346 | static int set_guid_rec(struct ib_device *ibdev, | ||
347 | u8 port, int index, | ||
348 | struct mlx4_sriov_alias_guid_info_rec_det *rec_det) | ||
349 | { | ||
350 | int err; | ||
351 | struct mlx4_ib_dev *dev = to_mdev(ibdev); | ||
352 | struct ib_sa_guidinfo_rec guid_info_rec; | ||
353 | ib_sa_comp_mask comp_mask; | ||
354 | struct ib_port_attr attr; | ||
355 | struct mlx4_alias_guid_work_context *callback_context; | ||
356 | unsigned long resched_delay, flags, flags1; | ||
357 | struct list_head *head = | ||
358 | &dev->sriov.alias_guid.ports_guid[port - 1].cb_list; | ||
359 | |||
360 | err = __mlx4_ib_query_port(ibdev, port, &attr, 1); | ||
361 | if (err) { | ||
362 | pr_debug("mlx4_ib_query_port failed (err: %d), port: %d\n", | ||
363 | err, port); | ||
364 | return err; | ||
365 | } | ||
366 | /*check the port was configured by the sm, otherwise no need to send */ | ||
367 | if (attr.state != IB_PORT_ACTIVE) { | ||
368 | pr_debug("port %d not active...rescheduling\n", port); | ||
369 | resched_delay = 5 * HZ; | ||
370 | err = -EAGAIN; | ||
371 | goto new_schedule; | ||
372 | } | ||
373 | |||
374 | callback_context = kmalloc(sizeof *callback_context, GFP_KERNEL); | ||
375 | if (!callback_context) { | ||
376 | err = -ENOMEM; | ||
377 | resched_delay = HZ * 5; | ||
378 | goto new_schedule; | ||
379 | } | ||
380 | callback_context->port = port; | ||
381 | callback_context->dev = dev; | ||
382 | callback_context->block_num = index; | ||
383 | |||
384 | memset(&guid_info_rec, 0, sizeof (struct ib_sa_guidinfo_rec)); | ||
385 | |||
386 | guid_info_rec.lid = cpu_to_be16(attr.lid); | ||
387 | guid_info_rec.block_num = index; | ||
388 | |||
389 | memcpy(guid_info_rec.guid_info_list, rec_det->all_recs, | ||
390 | GUID_REC_SIZE * NUM_ALIAS_GUID_IN_REC); | ||
391 | comp_mask = IB_SA_GUIDINFO_REC_LID | IB_SA_GUIDINFO_REC_BLOCK_NUM | | ||
392 | rec_det->guid_indexes; | ||
393 | |||
394 | init_completion(&callback_context->done); | ||
395 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
396 | list_add_tail(&callback_context->list, head); | ||
397 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
398 | |||
399 | callback_context->query_id = | ||
400 | ib_sa_guid_info_rec_query(dev->sriov.alias_guid.sa_client, | ||
401 | ibdev, port, &guid_info_rec, | ||
402 | comp_mask, rec_det->method, 1000, | ||
403 | GFP_KERNEL, aliasguid_query_handler, | ||
404 | callback_context, | ||
405 | &callback_context->sa_query); | ||
406 | if (callback_context->query_id < 0) { | ||
407 | pr_debug("ib_sa_guid_info_rec_query failed, query_id: " | ||
408 | "%d. will reschedule to the next 1 sec.\n", | ||
409 | callback_context->query_id); | ||
410 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
411 | list_del(&callback_context->list); | ||
412 | kfree(callback_context); | ||
413 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
414 | resched_delay = 1 * HZ; | ||
415 | err = -EAGAIN; | ||
416 | goto new_schedule; | ||
417 | } | ||
418 | err = 0; | ||
419 | goto out; | ||
420 | |||
421 | new_schedule: | ||
422 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); | ||
423 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
424 | invalidate_guid_record(dev, port, index); | ||
425 | if (!dev->sriov.is_going_down) { | ||
426 | queue_delayed_work(dev->sriov.alias_guid.ports_guid[port - 1].wq, | ||
427 | &dev->sriov.alias_guid.ports_guid[port - 1].alias_guid_work, | ||
428 | resched_delay); | ||
429 | } | ||
430 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
431 | spin_unlock_irqrestore(&dev->sriov.going_down_lock, flags); | ||
432 | |||
433 | out: | ||
434 | return err; | ||
435 | } | ||
436 | |||
437 | void mlx4_ib_invalidate_all_guid_record(struct mlx4_ib_dev *dev, int port) | ||
438 | { | ||
439 | int i; | ||
440 | unsigned long flags, flags1; | ||
441 | |||
442 | pr_debug("port %d\n", port); | ||
443 | |||
444 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); | ||
445 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
446 | for (i = 0; i < NUM_ALIAS_GUID_REC_IN_PORT; i++) | ||
447 | invalidate_guid_record(dev, port, i); | ||
448 | |||
449 | if (mlx4_is_master(dev->dev) && !dev->sriov.is_going_down) { | ||
450 | /* | ||
451 | make sure no work waits in the queue, if the work is already | ||
452 | queued(not on the timer) the cancel will fail. That is not a problem | ||
453 | because we just want the work started. | ||
454 | */ | ||
455 | cancel_delayed_work(&dev->sriov.alias_guid. | ||
456 | ports_guid[port - 1].alias_guid_work); | ||
457 | queue_delayed_work(dev->sriov.alias_guid.ports_guid[port - 1].wq, | ||
458 | &dev->sriov.alias_guid.ports_guid[port - 1].alias_guid_work, | ||
459 | 0); | ||
460 | } | ||
461 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
462 | spin_unlock_irqrestore(&dev->sriov.going_down_lock, flags); | ||
463 | } | ||
464 | |||
465 | /* The function returns the next record that was | ||
466 | * not configured (or failed to be configured) */ | ||
467 | static int get_next_record_to_update(struct mlx4_ib_dev *dev, u8 port, | ||
468 | struct mlx4_next_alias_guid_work *rec) | ||
469 | { | ||
470 | int j; | ||
471 | unsigned long flags; | ||
472 | |||
473 | for (j = 0; j < NUM_ALIAS_GUID_REC_IN_PORT; j++) { | ||
474 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags); | ||
475 | if (dev->sriov.alias_guid.ports_guid[port].all_rec_per_port[j].status == | ||
476 | MLX4_GUID_INFO_STATUS_IDLE) { | ||
477 | memcpy(&rec->rec_det, | ||
478 | &dev->sriov.alias_guid.ports_guid[port].all_rec_per_port[j], | ||
479 | sizeof (struct mlx4_sriov_alias_guid_info_rec_det)); | ||
480 | rec->port = port; | ||
481 | rec->block_num = j; | ||
482 | dev->sriov.alias_guid.ports_guid[port].all_rec_per_port[j].status = | ||
483 | MLX4_GUID_INFO_STATUS_PENDING; | ||
484 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags); | ||
485 | return 0; | ||
486 | } | ||
487 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags); | ||
488 | } | ||
489 | return -ENOENT; | ||
490 | } | ||
491 | |||
492 | static void set_administratively_guid_record(struct mlx4_ib_dev *dev, int port, | ||
493 | int rec_index, | ||
494 | struct mlx4_sriov_alias_guid_info_rec_det *rec_det) | ||
495 | { | ||
496 | dev->sriov.alias_guid.ports_guid[port].all_rec_per_port[rec_index].guid_indexes = | ||
497 | rec_det->guid_indexes; | ||
498 | memcpy(dev->sriov.alias_guid.ports_guid[port].all_rec_per_port[rec_index].all_recs, | ||
499 | rec_det->all_recs, NUM_ALIAS_GUID_IN_REC * GUID_REC_SIZE); | ||
500 | dev->sriov.alias_guid.ports_guid[port].all_rec_per_port[rec_index].status = | ||
501 | rec_det->status; | ||
502 | } | ||
503 | |||
504 | static void set_all_slaves_guids(struct mlx4_ib_dev *dev, int port) | ||
505 | { | ||
506 | int j; | ||
507 | struct mlx4_sriov_alias_guid_info_rec_det rec_det ; | ||
508 | |||
509 | for (j = 0 ; j < NUM_ALIAS_GUID_REC_IN_PORT ; j++) { | ||
510 | memset(rec_det.all_recs, 0, NUM_ALIAS_GUID_IN_REC * GUID_REC_SIZE); | ||
511 | rec_det.guid_indexes = (!j ? 0 : IB_SA_GUIDINFO_REC_GID0) | | ||
512 | IB_SA_GUIDINFO_REC_GID1 | IB_SA_GUIDINFO_REC_GID2 | | ||
513 | IB_SA_GUIDINFO_REC_GID3 | IB_SA_GUIDINFO_REC_GID4 | | ||
514 | IB_SA_GUIDINFO_REC_GID5 | IB_SA_GUIDINFO_REC_GID6 | | ||
515 | IB_SA_GUIDINFO_REC_GID7; | ||
516 | rec_det.status = MLX4_GUID_INFO_STATUS_IDLE; | ||
517 | set_administratively_guid_record(dev, port, j, &rec_det); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | static void alias_guid_work(struct work_struct *work) | ||
522 | { | ||
523 | struct delayed_work *delay = to_delayed_work(work); | ||
524 | int ret = 0; | ||
525 | struct mlx4_next_alias_guid_work *rec; | ||
526 | struct mlx4_sriov_alias_guid_port_rec_det *sriov_alias_port = | ||
527 | container_of(delay, struct mlx4_sriov_alias_guid_port_rec_det, | ||
528 | alias_guid_work); | ||
529 | struct mlx4_sriov_alias_guid *sriov_alias_guid = sriov_alias_port->parent; | ||
530 | struct mlx4_ib_sriov *ib_sriov = container_of(sriov_alias_guid, | ||
531 | struct mlx4_ib_sriov, | ||
532 | alias_guid); | ||
533 | struct mlx4_ib_dev *dev = container_of(ib_sriov, struct mlx4_ib_dev, sriov); | ||
534 | |||
535 | rec = kzalloc(sizeof *rec, GFP_KERNEL); | ||
536 | if (!rec) { | ||
537 | pr_err("alias_guid_work: No Memory\n"); | ||
538 | return; | ||
539 | } | ||
540 | |||
541 | pr_debug("starting [port: %d]...\n", sriov_alias_port->port + 1); | ||
542 | ret = get_next_record_to_update(dev, sriov_alias_port->port, rec); | ||
543 | if (ret) { | ||
544 | pr_debug("No more records to update.\n"); | ||
545 | goto out; | ||
546 | } | ||
547 | |||
548 | set_guid_rec(&dev->ib_dev, rec->port + 1, rec->block_num, | ||
549 | &rec->rec_det); | ||
550 | |||
551 | out: | ||
552 | kfree(rec); | ||
553 | } | ||
554 | |||
555 | |||
556 | void mlx4_ib_init_alias_guid_work(struct mlx4_ib_dev *dev, int port) | ||
557 | { | ||
558 | unsigned long flags, flags1; | ||
559 | |||
560 | if (!mlx4_is_master(dev->dev)) | ||
561 | return; | ||
562 | spin_lock_irqsave(&dev->sriov.going_down_lock, flags); | ||
563 | spin_lock_irqsave(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
564 | if (!dev->sriov.is_going_down) { | ||
565 | queue_delayed_work(dev->sriov.alias_guid.ports_guid[port].wq, | ||
566 | &dev->sriov.alias_guid.ports_guid[port].alias_guid_work, 0); | ||
567 | } | ||
568 | spin_unlock_irqrestore(&dev->sriov.alias_guid.ag_work_lock, flags1); | ||
569 | spin_unlock_irqrestore(&dev->sriov.going_down_lock, flags); | ||
570 | } | ||
571 | |||
572 | void mlx4_ib_destroy_alias_guid_service(struct mlx4_ib_dev *dev) | ||
573 | { | ||
574 | int i; | ||
575 | struct mlx4_ib_sriov *sriov = &dev->sriov; | ||
576 | struct mlx4_alias_guid_work_context *cb_ctx; | ||
577 | struct mlx4_sriov_alias_guid_port_rec_det *det; | ||
578 | struct ib_sa_query *sa_query; | ||
579 | unsigned long flags; | ||
580 | |||
581 | for (i = 0 ; i < dev->num_ports; i++) { | ||
582 | cancel_delayed_work(&dev->sriov.alias_guid.ports_guid[i].alias_guid_work); | ||
583 | det = &sriov->alias_guid.ports_guid[i]; | ||
584 | spin_lock_irqsave(&sriov->alias_guid.ag_work_lock, flags); | ||
585 | while (!list_empty(&det->cb_list)) { | ||
586 | cb_ctx = list_entry(det->cb_list.next, | ||
587 | struct mlx4_alias_guid_work_context, | ||
588 | list); | ||
589 | sa_query = cb_ctx->sa_query; | ||
590 | cb_ctx->sa_query = NULL; | ||
591 | list_del(&cb_ctx->list); | ||
592 | spin_unlock_irqrestore(&sriov->alias_guid.ag_work_lock, flags); | ||
593 | ib_sa_cancel_query(cb_ctx->query_id, sa_query); | ||
594 | wait_for_completion(&cb_ctx->done); | ||
595 | kfree(cb_ctx); | ||
596 | spin_lock_irqsave(&sriov->alias_guid.ag_work_lock, flags); | ||
597 | } | ||
598 | spin_unlock_irqrestore(&sriov->alias_guid.ag_work_lock, flags); | ||
599 | } | ||
600 | for (i = 0 ; i < dev->num_ports; i++) { | ||
601 | flush_workqueue(dev->sriov.alias_guid.ports_guid[i].wq); | ||
602 | destroy_workqueue(dev->sriov.alias_guid.ports_guid[i].wq); | ||
603 | } | ||
604 | ib_sa_unregister_client(dev->sriov.alias_guid.sa_client); | ||
605 | kfree(dev->sriov.alias_guid.sa_client); | ||
606 | } | ||
607 | |||
608 | int mlx4_ib_init_alias_guid_service(struct mlx4_ib_dev *dev) | ||
609 | { | ||
610 | char alias_wq_name[15]; | ||
611 | int ret = 0; | ||
612 | int i, j, k; | ||
613 | union ib_gid gid; | ||
614 | |||
615 | if (!mlx4_is_master(dev->dev)) | ||
616 | return 0; | ||
617 | dev->sriov.alias_guid.sa_client = | ||
618 | kzalloc(sizeof *dev->sriov.alias_guid.sa_client, GFP_KERNEL); | ||
619 | if (!dev->sriov.alias_guid.sa_client) | ||
620 | return -ENOMEM; | ||
621 | |||
622 | ib_sa_register_client(dev->sriov.alias_guid.sa_client); | ||
623 | |||
624 | spin_lock_init(&dev->sriov.alias_guid.ag_work_lock); | ||
625 | |||
626 | for (i = 1; i <= dev->num_ports; ++i) { | ||
627 | if (dev->ib_dev.query_gid(&dev->ib_dev , i, 0, &gid)) { | ||
628 | ret = -EFAULT; | ||
629 | goto err_unregister; | ||
630 | } | ||
631 | } | ||
632 | |||
633 | for (i = 0 ; i < dev->num_ports; i++) { | ||
634 | memset(&dev->sriov.alias_guid.ports_guid[i], 0, | ||
635 | sizeof (struct mlx4_sriov_alias_guid_port_rec_det)); | ||
636 | /*Check if the SM doesn't need to assign the GUIDs*/ | ||
637 | for (j = 0; j < NUM_ALIAS_GUID_REC_IN_PORT; j++) { | ||
638 | if (mlx4_ib_sm_guid_assign) { | ||
639 | dev->sriov.alias_guid.ports_guid[i]. | ||
640 | all_rec_per_port[j]. | ||
641 | ownership = MLX4_GUID_DRIVER_ASSIGN; | ||
642 | continue; | ||
643 | } | ||
644 | dev->sriov.alias_guid.ports_guid[i].all_rec_per_port[j]. | ||
645 | ownership = MLX4_GUID_NONE_ASSIGN; | ||
646 | /*mark each val as it was deleted, | ||
647 | till the sysAdmin will give it valid val*/ | ||
648 | for (k = 0; k < NUM_ALIAS_GUID_IN_REC; k++) { | ||
649 | *(__be64 *)&dev->sriov.alias_guid.ports_guid[i]. | ||
650 | all_rec_per_port[j].all_recs[GUID_REC_SIZE * k] = | ||
651 | cpu_to_be64(MLX4_GUID_FOR_DELETE_VAL); | ||
652 | } | ||
653 | } | ||
654 | INIT_LIST_HEAD(&dev->sriov.alias_guid.ports_guid[i].cb_list); | ||
655 | /*prepare the records, set them to be allocated by sm*/ | ||
656 | for (j = 0 ; j < NUM_ALIAS_GUID_REC_IN_PORT; j++) | ||
657 | invalidate_guid_record(dev, i + 1, j); | ||
658 | |||
659 | dev->sriov.alias_guid.ports_guid[i].parent = &dev->sriov.alias_guid; | ||
660 | dev->sriov.alias_guid.ports_guid[i].port = i; | ||
661 | if (mlx4_ib_sm_guid_assign) | ||
662 | set_all_slaves_guids(dev, i); | ||
663 | |||
664 | snprintf(alias_wq_name, sizeof alias_wq_name, "alias_guid%d", i); | ||
665 | dev->sriov.alias_guid.ports_guid[i].wq = | ||
666 | create_singlethread_workqueue(alias_wq_name); | ||
667 | if (!dev->sriov.alias_guid.ports_guid[i].wq) { | ||
668 | ret = -ENOMEM; | ||
669 | goto err_thread; | ||
670 | } | ||
671 | INIT_DELAYED_WORK(&dev->sriov.alias_guid.ports_guid[i].alias_guid_work, | ||
672 | alias_guid_work); | ||
673 | } | ||
674 | return 0; | ||
675 | |||
676 | err_thread: | ||
677 | for (--i; i >= 0; i--) { | ||
678 | destroy_workqueue(dev->sriov.alias_guid.ports_guid[i].wq); | ||
679 | dev->sriov.alias_guid.ports_guid[i].wq = NULL; | ||
680 | } | ||
681 | |||
682 | err_unregister: | ||
683 | ib_sa_unregister_client(dev->sriov.alias_guid.sa_client); | ||
684 | kfree(dev->sriov.alias_guid.sa_client); | ||
685 | dev->sriov.alias_guid.sa_client = NULL; | ||
686 | pr_err("init_alias_guid_service: Failed. (ret:%d)\n", ret); | ||
687 | return ret; | ||
688 | } | ||