Commit | Line | Data |
---|---|---|
a0c64a17 JM |
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 | ||
c1e7e466 | 116 | ib_sa_comp_mask mlx4_ib_get_aguid_comp_mask_from_ix(int index) |
a0c64a17 JM |
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; | |
c1e7e466 | 262 | rec->guid_indexes |= mlx4_ib_get_aguid_comp_mask_from_ix(i); |
a0c64a17 JM |
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; | |
c1e7e466 | 340 | comp_mask |= mlx4_ib_get_aguid_comp_mask_from_ix(i); |
a0c64a17 JM |
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 | } |