]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ofed/opensm/opensm/osm_pkey_mgr.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb, and openmp
[FreeBSD/FreeBSD.git] / contrib / ofed / opensm / opensm / osm_pkey_mgr.c
1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2011 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved.
6  *
7  * This software is available to you under a choice of one of two
8  * licenses.  You may choose to be licensed under the terms of the GNU
9  * General Public License (GPL) Version 2, available from the file
10  * COPYING in the main directory of this source tree, or the
11  * OpenIB.org BSD license below:
12  *
13  *     Redistribution and use in source and binary forms, with or
14  *     without modification, are permitted provided that the following
15  *     conditions are met:
16  *
17  *      - Redistributions of source code must retain the above
18  *        copyright notice, this list of conditions and the following
19  *        disclaimer.
20  *
21  *      - Redistributions in binary form must reproduce the above
22  *        copyright notice, this list of conditions and the following
23  *        disclaimer in the documentation and/or other materials
24  *        provided with the distribution.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33  * SOFTWARE.
34  *
35  */
36
37 /*
38  * Abstract:
39  * Implementation of the P_Key Manager (Partition Manager).
40  * This is part of the OpenSM.
41  */
42
43 #if HAVE_CONFIG_H
44 #  include <config.h>
45 #endif                          /* HAVE_CONFIG_H */
46
47 #include <string.h>
48 #include <iba/ib_types.h>
49 #include <complib/cl_qmap.h>
50 #include <complib/cl_debug.h>
51 #include <opensm/osm_file_ids.h>
52 #define FILE_ID OSM_FILE_PKEY_MGR_C
53 #include <opensm/osm_node.h>
54 #include <opensm/osm_switch.h>
55 #include <opensm/osm_partition.h>
56 #include <opensm/osm_opensm.h>
57
58 static void clear_accum_pkey_index(osm_pkey_tbl_t * p_pkey_tbl,
59                                    uint16_t pkey_index);
60
61 /*
62   The max number of pkeys/pkey blocks for a physical port is located
63   in a different place for switch external ports (SwitchInfo) and the
64   rest of the ports (NodeInfo).
65 */
66 static uint16_t
67 pkey_mgr_get_physp_max_pkeys(IN const osm_physp_t * p_physp)
68 {
69         osm_node_t *p_node = osm_physp_get_node_ptr(p_physp);
70         uint16_t num_pkeys = 0;
71
72         if (!p_node->sw || (osm_physp_get_port_num(p_physp) == 0))
73                 num_pkeys = cl_ntoh16(p_node->node_info.partition_cap);
74         else
75                 num_pkeys = cl_ntoh16(p_node->sw->switch_info.enforce_cap);
76         return num_pkeys;
77 }
78
79 static uint16_t
80 pkey_mgr_get_physp_max_blocks(IN const osm_physp_t * p_physp)
81 {
82         return ((pkey_mgr_get_physp_max_pkeys(p_physp) + 31) / 32);
83 }
84
85 /*
86  * Insert new pending pkey entry to the specific port pkey table
87  * pending pkeys. New entries are inserted at the back.
88  */
89 static void
90 pkey_mgr_process_physical_port(IN osm_log_t * p_log,
91                                IN osm_sm_t * sm,
92                                IN const ib_net16_t pkey,
93                                IN osm_physp_t * p_physp)
94 {
95         osm_node_t *p_node = osm_physp_get_node_ptr(p_physp);
96         osm_pkey_tbl_t *p_pkey_tbl;
97         ib_net16_t *p_orig_pkey;
98         osm_pending_pkey_t *p_pending;
99
100         p_pkey_tbl = &p_physp->pkeys;
101         p_pending = (osm_pending_pkey_t *) calloc(1, sizeof(osm_pending_pkey_t));
102         if (!p_pending) {
103                 OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0502: "
104                         "Failed to allocate new pending pkey entry for node "
105                         "0x%016" PRIx64 " port %u\n",
106                         cl_ntoh64(osm_node_get_node_guid(p_node)),
107                         osm_physp_get_port_num(p_physp));
108                 return;
109         }
110         p_pending->pkey = pkey;
111         if (sm->p_subn->opt.allow_both_pkeys)
112                 p_orig_pkey = cl_map_get(&p_pkey_tbl->keys, pkey);
113         else
114                 p_orig_pkey = cl_map_get(&p_pkey_tbl->keys,
115                                          ib_pkey_get_base(pkey));
116
117         if (!p_orig_pkey) {
118                 p_pending->is_new = TRUE;
119         } else {
120                 CL_ASSERT(ib_pkey_get_base(*p_orig_pkey) ==
121                           ib_pkey_get_base(pkey));
122                 p_pending->is_new = FALSE;
123                 if (osm_pkey_tbl_get_block_and_idx(p_pkey_tbl, p_orig_pkey,
124                                                    &p_pending->block,
125                                                    &p_pending->index) !=
126                     IB_SUCCESS) {
127                         OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0503: "
128                                 "Failed to obtain P_Key 0x%04x block and index "
129                                 "for node 0x%016" PRIx64 " port %u\n",
130                                 cl_ntoh16(ib_pkey_get_base(pkey)),
131                                 cl_ntoh64(osm_node_get_node_guid(p_node)),
132                                 osm_physp_get_port_num(p_physp));
133                         free(p_pending);
134                         return;
135                 }
136                 if (p_physp->pkeys.indx0_pkey) {
137                         /*
138                          * Remove the pkey that should be at index 0 from
139                          * accum pkey if current position is not index 0
140                          */
141                         if (((sm->p_subn->opt.allow_both_pkeys &&
142                               pkey == p_physp->pkeys.indx0_pkey) ||
143                              (!sm->p_subn->opt.allow_both_pkeys &&
144                               ib_pkey_get_base(pkey) == ib_pkey_get_base(p_physp->pkeys.indx0_pkey))) &&
145                             (p_pending->block != 0 || p_pending->index != 0)) {
146                                         p_pending->is_new = TRUE;
147                                         clear_accum_pkey_index(p_pkey_tbl,
148                                                                p_pending->block *
149                                                                IB_NUM_PKEY_ELEMENTS_IN_BLOCK +
150                                                                p_pending->index);
151                         }
152
153                         if (p_pending->block == 0 && p_pending->index == 0) {
154                                 /* Move the pkey away from index 0 */
155                                 if ((sm->p_subn->opt.allow_both_pkeys &&
156                                      pkey != p_physp->pkeys.indx0_pkey) ||
157                                     (!sm->p_subn->opt.allow_both_pkeys &&
158                                      ib_pkey_get_base(pkey) != ib_pkey_get_base(p_physp->pkeys.indx0_pkey))) {
159                                         p_pending->is_new = TRUE;
160                                         clear_accum_pkey_index(p_pkey_tbl, 0);
161                                 }
162                         }
163                  } else {
164                         /* If index 0 is occupied by non-default, it should reoccupied by pkey 0x7FFF */
165                         if (p_pending->block == 0 && p_pending->index == 0) {
166                                 if (ib_pkey_get_base(pkey) != IB_DEFAULT_PARTIAL_PKEY) {
167                                         p_pending->is_new = TRUE;
168                                         clear_accum_pkey_index(p_pkey_tbl, 0);
169                                 }
170                         /* Need to move default pkey to index 0 */
171                         } else if ((sm->p_subn->opt.allow_both_pkeys &&
172                                     pkey == IB_DEFAULT_PKEY) ||
173                                    (!sm->p_subn->opt.allow_both_pkeys &&
174                                     ib_pkey_get_base(pkey) == IB_DEFAULT_PARTIAL_PKEY)) {
175                                         p_pending->is_new = TRUE;
176                                         clear_accum_pkey_index(p_pkey_tbl,
177                                                                p_pending->block *
178                                                                IB_NUM_PKEY_ELEMENTS_IN_BLOCK +
179                                                                p_pending->index);
180                         }
181                 }
182
183         }
184         if (p_pending->is_new == TRUE)
185                  cl_qlist_insert_tail(&p_pkey_tbl->pending,
186                                       (cl_list_item_t *) p_pending);
187         else
188                 cl_qlist_insert_head(&p_pkey_tbl->pending,
189                                      (cl_list_item_t *) p_pending);
190
191         OSM_LOG(p_log, OSM_LOG_DEBUG,
192                 "pkey 0x%04x was %s for node 0x%016" PRIx64 " port %u\n",
193                 cl_ntoh16(pkey), p_pending->is_new ? "inserted" : "updated",
194                 cl_ntoh64(osm_node_get_node_guid(p_node)),
195                 osm_physp_get_port_num(p_physp));
196 }
197
198 static void
199 pkey_mgr_process_partition_table(osm_log_t * p_log, osm_sm_t * sm,
200                                  const osm_prtn_t * p_prtn,
201                                  const boolean_t full)
202 {
203         const cl_map_t *p_tbl =
204             full ? &p_prtn->full_guid_tbl : &p_prtn->part_guid_tbl;
205         cl_map_iterator_t i, i_next;
206         ib_net16_t pkey = p_prtn->pkey;
207         osm_physp_t *p_physp;
208
209         if (full)
210                 pkey |= cl_hton16(0x8000);
211
212         i_next = cl_map_head(p_tbl);
213         while (i_next != cl_map_end(p_tbl)) {
214                 i = i_next;
215                 i_next = cl_map_next(i);
216                 p_physp = cl_map_obj(i);
217                 if (p_physp)
218                         pkey_mgr_process_physical_port(p_log, sm, pkey,
219                                                        p_physp);
220         }
221 }
222
223 static ib_api_status_t
224 pkey_mgr_update_pkey_entry(IN osm_sm_t * sm,
225                            IN const osm_physp_t * p_physp,
226                            IN const ib_pkey_table_t * block,
227                            IN const uint16_t block_index)
228 {
229         osm_madw_context_t context;
230         osm_node_t *p_node = osm_physp_get_node_ptr(p_physp);
231         osm_physp_t *physp0;
232         uint32_t attr_mod;
233         ib_net64_t m_key;
234
235         context.pkey_context.node_guid = osm_node_get_node_guid(p_node);
236         context.pkey_context.port_guid = osm_physp_get_port_guid(p_physp);
237         context.pkey_context.set_method = TRUE;
238         attr_mod = block_index;
239         if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH &&
240             osm_physp_get_port_num(p_physp) != 0) {
241                 attr_mod |= osm_physp_get_port_num(p_physp) << 16;
242                 physp0 = osm_node_get_physp_ptr(p_node, 0);
243                 m_key = ib_port_info_get_m_key(&physp0->port_info);
244         } else
245                 m_key = ib_port_info_get_m_key(&p_physp->port_info);
246         return osm_req_set(sm, osm_physp_get_dr_path_ptr(p_physp),
247                            (uint8_t *) block, sizeof(*block),
248                            IB_MAD_ATTR_P_KEY_TABLE,
249                            cl_hton32(attr_mod), FALSE, m_key,
250                            CL_DISP_MSGID_NONE, &context);
251 }
252
253 static ib_api_status_t
254 pkey_mgr_enforce_partition(IN osm_log_t * p_log, osm_sm_t * sm,
255                            IN osm_physp_t * p_physp,
256                            IN osm_partition_enforce_type_enum enforce_type)
257 {
258         osm_madw_context_t context;
259         uint8_t payload[IB_SMP_DATA_SIZE];
260         ib_port_info_t *p_pi;
261         ib_net64_t m_key;
262         osm_physp_t *physp0;
263         ib_api_status_t status;
264         uint8_t enforce_bits;
265
266         p_pi = &p_physp->port_info;
267
268         if (enforce_type == OSM_PARTITION_ENFORCE_TYPE_BOTH)
269                 enforce_bits = 0xc;
270         else if (enforce_type == OSM_PARTITION_ENFORCE_TYPE_IN)
271                 enforce_bits = 0x8;
272         else
273                 enforce_bits = 0x4;
274
275         if ((p_pi->vl_enforce & 0xc) == enforce_bits *
276             (enforce_type != OSM_PARTITION_ENFORCE_TYPE_OFF)) {
277                 OSM_LOG(p_log, OSM_LOG_DEBUG,
278                         "No need to update PortInfo for "
279                         "node 0x%016" PRIx64 " port %u (%s)\n",
280                         cl_ntoh64(osm_node_get_node_guid
281                                   (osm_physp_get_node_ptr(p_physp))),
282                         osm_physp_get_port_num(p_physp),
283                         p_physp->p_node->print_desc);
284                 return IB_SUCCESS;
285         }
286
287         memcpy(payload, p_pi, sizeof(ib_port_info_t));
288
289         p_pi = (ib_port_info_t *) payload;
290         p_pi->vl_enforce &= ~0xc;
291         if (enforce_type != OSM_PARTITION_ENFORCE_TYPE_OFF)
292                 p_pi->vl_enforce |= enforce_bits;
293
294         p_pi->state_info2 = 0;
295         ib_port_info_set_port_state(p_pi, IB_LINK_NO_CHANGE);
296
297         physp0 = osm_node_get_physp_ptr(p_physp->p_node, 0);
298         m_key = ib_port_info_get_m_key(&physp0->port_info);
299
300         context.pi_context.node_guid =
301             osm_node_get_node_guid(osm_physp_get_node_ptr(p_physp));
302         context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
303         context.pi_context.set_method = TRUE;
304         context.pi_context.light_sweep = FALSE;
305         context.pi_context.active_transition = FALSE;
306         context.pi_context.client_rereg = FALSE;
307
308         status = osm_req_set(sm, osm_physp_get_dr_path_ptr(p_physp),
309                              payload, sizeof(payload),
310                              IB_MAD_ATTR_PORT_INFO,
311                              cl_hton32(osm_physp_get_port_num(p_physp)),
312                              FALSE, m_key,
313                              CL_DISP_MSGID_NONE, &context);
314         if (status != IB_SUCCESS)
315                 OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0511: "
316                         "Failed to set PortInfo for "
317                         "node 0x%016" PRIx64 " port %u (%s)\n",
318                         cl_ntoh64(osm_node_get_node_guid
319                                   (osm_physp_get_node_ptr(p_physp))),
320                         osm_physp_get_port_num(p_physp),
321                         p_physp->p_node->print_desc);
322         else
323                 OSM_LOG(p_log, OSM_LOG_DEBUG,
324                         "Set PortInfo for node 0x%016" PRIx64 " port %u (%s)\n",
325                         cl_ntoh64(osm_node_get_node_guid
326                                   (osm_physp_get_node_ptr(p_physp))),
327                         osm_physp_get_port_num(p_physp),
328                         p_physp->p_node->print_desc);
329         return status;
330 }
331
332 static void clear_accum_pkey_index(osm_pkey_tbl_t * p_pkey_tbl,
333                                    uint16_t pkey_index)
334 {
335         uint16_t pkey_idx_bias, pkey_idx;
336         void *ptr;
337         uintptr_t pkey_idx_ptr;
338         cl_map_iterator_t map_iter, map_iter_temp;
339
340         map_iter = cl_map_head(&p_pkey_tbl->accum_pkeys);
341
342         pkey_idx_bias = pkey_index + 1; // adjust for pkey index bias in accum_pkeys
343
344         while (map_iter != cl_map_end(&p_pkey_tbl->accum_pkeys)) {
345                 map_iter_temp = cl_map_next(map_iter);
346                 ptr = (uint16_t *) cl_map_obj(map_iter);
347                 CL_ASSERT(ptr);
348                 pkey_idx_ptr = (uintptr_t) ptr;
349                 pkey_idx = pkey_idx_ptr;
350                 if (pkey_idx == pkey_idx_bias) {
351                         cl_map_remove_item(&p_pkey_tbl->accum_pkeys, map_iter);
352                         if (p_pkey_tbl->last_pkey_idx == pkey_idx)
353                                 osm_pkey_find_last_accum_pkey_index(p_pkey_tbl);
354                         break;
355                 }
356                 map_iter = map_iter_temp;
357         }
358 }
359
360 static int last_accum_pkey_index(osm_pkey_tbl_t * p_pkey_tbl,
361                                  uint16_t * p_block_idx,
362                                  uint8_t * p_pkey_idx)
363 {
364         if (p_pkey_tbl->last_pkey_idx) {
365                 *p_block_idx = (p_pkey_tbl->last_pkey_idx - 1) / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
366                 *p_pkey_idx = (p_pkey_tbl->last_pkey_idx - 1) % IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
367                 return 1;
368         }
369
370         return 0;
371 }
372
373 static int pkey_mgr_update_port(osm_log_t * p_log, osm_sm_t * sm,
374                                 const osm_port_t * const p_port)
375 {
376         osm_physp_t *p_physp;
377         osm_node_t *p_node;
378         ib_pkey_table_t *block, *new_block;
379         osm_pkey_tbl_t *p_pkey_tbl;
380         uint16_t block_index;
381         uint8_t pkey_index;
382         uint16_t last_free_block_index = 0;
383         uint8_t last_free_pkey_index = 0;
384         uint16_t num_of_blocks;
385         uint16_t max_num_of_blocks;
386         ib_api_status_t status;
387         osm_pending_pkey_t *p_pending;
388         boolean_t found;
389         ib_pkey_table_t empty_block;
390         int ret = 0, full = 0;
391         void *ptr;
392         uintptr_t pkey_idx_ptr;
393         uint16_t pkey_idx;
394
395         p_physp = p_port->p_physp;
396         if (!p_physp)
397                 return FALSE;
398
399         memset(&empty_block, 0, sizeof(ib_pkey_table_t));
400
401         p_node = osm_physp_get_node_ptr(p_physp);
402         p_pkey_tbl = &p_physp->pkeys;
403         num_of_blocks = osm_pkey_tbl_get_num_blocks(p_pkey_tbl);
404         max_num_of_blocks = pkey_mgr_get_physp_max_blocks(p_physp);
405         if (p_pkey_tbl->max_blocks > max_num_of_blocks) {
406                 OSM_LOG(p_log, OSM_LOG_INFO,
407                         "Max number of blocks reduced from %u to %u "
408                         "for node 0x%016" PRIx64 " port %u (%s)\n",
409                         p_pkey_tbl->max_blocks, max_num_of_blocks,
410                         cl_ntoh64(osm_node_get_node_guid(p_node)),
411                         osm_physp_get_port_num(p_physp),
412                         p_physp->p_node->print_desc);
413         }
414         p_pkey_tbl->max_blocks = max_num_of_blocks;
415
416         osm_pkey_tbl_init_new_blocks(p_pkey_tbl);
417         p_pkey_tbl->used_blocks = 0;
418
419         /*
420            process every pending pkey in order -
421            first must be "updated" last are "new"
422          */
423         p_pending =
424             (osm_pending_pkey_t *) cl_qlist_remove_head(&p_pkey_tbl->pending);
425         while (p_pending !=
426                (osm_pending_pkey_t *) cl_qlist_end(&p_pkey_tbl->pending)) {
427
428                 found = FALSE;
429                 ptr = NULL;
430
431                 if (p_pending->is_new == FALSE) {
432                         block_index = p_pending->block;
433                         pkey_index = p_pending->index;
434                         found = TRUE;
435                 } else {
436                         ptr = cl_map_get(&p_pkey_tbl->accum_pkeys,p_pending->pkey);
437                         if (ptr != NULL) {
438                                 pkey_idx_ptr = (uintptr_t) ptr;
439                                 pkey_idx = pkey_idx_ptr;
440                                 pkey_idx--; /* adjust pkey index for bias */
441                                 block_index = pkey_idx / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
442                                 pkey_index = pkey_idx % IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
443
444                                 if (((sm->p_subn->opt.allow_both_pkeys &&
445                                       p_pending->pkey == p_physp->pkeys.indx0_pkey) ||
446                                      (!sm->p_subn->opt.allow_both_pkeys &&
447                                       ib_pkey_get_base(p_pending->pkey) == ib_pkey_get_base(p_physp->pkeys.indx0_pkey))) ||
448                                     ((p_pending->pkey != p_physp->pkeys.indx0_pkey &&
449                                       pkey_idx == 0))) {
450                                         clear_accum_pkey_index(p_pkey_tbl, pkey_idx);
451                                         cl_qlist_insert_tail(&p_pkey_tbl->pending,
452                                                              (cl_list_item_t *)p_pending);
453                                         p_pending =
454                                             (osm_pending_pkey_t *) cl_qlist_remove_head(&p_pkey_tbl->pending);
455                                         continue;
456                                 } else
457                                         found = TRUE;
458                         }
459
460                         if (!found) {
461                                 if (!p_pkey_tbl->indx0_pkey &&
462                                     ((sm->p_subn->opt.allow_both_pkeys &&
463                                       p_pending->pkey == IB_DEFAULT_PKEY) ||
464                                      (!sm->p_subn->opt.allow_both_pkeys &&
465                                       ib_pkey_get_base(p_pending->pkey) == IB_DEFAULT_PARTIAL_PKEY))) {
466                                         block_index = 0;
467                                         pkey_index = 0;
468                                 } else if ((sm->p_subn->opt.allow_both_pkeys &&
469                                             p_pending->pkey == p_pkey_tbl->indx0_pkey) ||
470                                            (!sm->p_subn->opt.allow_both_pkeys &&
471                                             ib_pkey_get_base(p_pending->pkey) ==
472                                             ib_pkey_get_base(p_pkey_tbl->indx0_pkey))) {
473                                         block_index = 0;
474                                         pkey_index = 0;
475                                 } else if (last_accum_pkey_index(p_pkey_tbl,
476                                                                  &last_free_block_index,
477                                                                  &last_free_pkey_index)) {
478                                         block_index = last_free_block_index;
479                                         pkey_index = last_free_pkey_index + 1;
480                                         if (pkey_index >= IB_NUM_PKEY_ELEMENTS_IN_BLOCK) {
481                                                 block_index++;
482                                                 pkey_index -= IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
483                                         }
484                                 } else {
485                                         block_index = 0;
486                                         pkey_index = 1;
487                                 }
488
489                                 if (block_index * IB_NUM_PKEY_ELEMENTS_IN_BLOCK + pkey_index >= pkey_mgr_get_physp_max_pkeys(p_physp)) {
490                                         if ((sm->p_subn->opt.allow_both_pkeys &&
491                                              p_pending->pkey != IB_DEFAULT_PKEY) ||
492                                             (!sm->p_subn->opt.allow_both_pkeys &&
493                                              ib_pkey_get_base(p_pending->pkey) != IB_DEFAULT_PARTIAL_PKEY)) {
494                                                 last_free_block_index = 0;
495                                                 last_free_pkey_index = 1;
496                                                 found = osm_pkey_find_next_free_entry(p_pkey_tbl, &last_free_block_index, &last_free_pkey_index);
497                                         } else
498                                                 found = FALSE;
499                                         if (!found)
500                                                 full = 1;
501                                         else {
502                                                 block_index = last_free_block_index;
503                                                 pkey_index = last_free_pkey_index;
504                                                 if (block_index * IB_NUM_PKEY_ELEMENTS_IN_BLOCK + pkey_index >= pkey_mgr_get_physp_max_pkeys(p_physp)) {
505                                                         full = 1;
506                                                         found = FALSE;
507                                                 } else {
508                                                         OSM_LOG(p_log, OSM_LOG_INFO,
509                                                                 "Reusing PKeyTable block index %u pkey index %u "
510                                                                 "for pkey 0x%x on 0x%016" PRIx64 " port %u (%s)\n",
511                                                                 block_index,
512                                                                 pkey_index,
513                                                                 cl_ntoh16(p_pending->pkey),
514                                                                 cl_ntoh64(osm_node_get_node_guid(p_node)),
515                                                                 osm_physp_get_port_num(p_physp),
516                                                                 p_physp->p_node->print_desc);
517
518                                                         clear_accum_pkey_index(p_pkey_tbl, block_index * IB_NUM_PKEY_ELEMENTS_IN_BLOCK + pkey_index);
519                                                 }
520                                         }
521                                         if (full)
522                                                 OSM_LOG(p_log, OSM_LOG_ERROR,
523                                                         "ERR 0512: "
524                                                         "Failed to set PKey 0x%04x because Pkey table is full "
525                                                         "for node 0x%016" PRIx64 " port %u (%s)\n",
526                                                         cl_ntoh16(p_pending->pkey),
527                                                         cl_ntoh64(osm_node_get_node_guid(p_node)),
528                                                         osm_physp_get_port_num(p_physp),
529                                                         p_physp->p_node->print_desc);
530                                 } else
531                                         found = TRUE;
532                         }
533                 }
534
535                 if (found) {
536                         if (IB_SUCCESS !=
537                             osm_pkey_tbl_set_new_entry(p_pkey_tbl, block_index,
538                                                        pkey_index,
539                                                        p_pending->pkey)) {
540                                 OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0505: "
541                                         "Failed to set PKey 0x%04x in block %u idx %u "
542                                         "for node 0x%016" PRIx64 " port %u (%s)\n",
543                                         cl_ntoh16(p_pending->pkey), block_index,
544                                         pkey_index,
545                                         cl_ntoh64(osm_node_get_node_guid
546                                                   (p_node)),
547                                         osm_physp_get_port_num(p_physp),
548                                         p_physp->p_node->print_desc);
549                         }
550                         if (ptr == NULL &&
551                             CL_SUCCESS !=
552                             osm_pkey_tbl_set_accum_pkeys(p_pkey_tbl,
553                                                          p_pending->pkey,
554                                                          block_index * IB_NUM_PKEY_ELEMENTS_IN_BLOCK + pkey_index)) {
555                                 OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0508: "
556                                         "Failed to set accum_pkeys PKey 0x%04x "
557                                         "in block %u idx %u for node 0x%016"
558                                         PRIx64 " port %u (%s)\n",
559                                         cl_ntoh16(p_pending->pkey), block_index,
560                                         pkey_index,
561                                         cl_ntoh64(osm_node_get_node_guid(p_node)),
562                                         osm_physp_get_port_num(p_physp),
563                                         p_physp->p_node->print_desc);
564                         }
565                 }
566                 free(p_pending);
567                 p_pending =
568                     (osm_pending_pkey_t *) cl_qlist_remove_head(&p_pkey_tbl->
569                                                                 pending);
570         }
571
572         p_pkey_tbl->indx0_pkey = 0;
573         /* now look for changes and store */
574         for (block_index = 0; block_index < num_of_blocks; block_index++) {
575                 block = osm_pkey_tbl_block_get(p_pkey_tbl, block_index);
576                 new_block = osm_pkey_tbl_new_block_get(p_pkey_tbl, block_index);
577                 if (!new_block)
578                         new_block = &empty_block;
579                 if (block && !memcmp(new_block, block, sizeof(*block)))
580                         continue;
581
582                 status =
583                     pkey_mgr_update_pkey_entry(sm, p_physp, new_block,
584                                                block_index);
585                 if (status == IB_SUCCESS)
586                         OSM_LOG(p_log, OSM_LOG_DEBUG,
587                                 "Updated pkey table block %u for node 0x%016"
588                                 PRIx64 " port %u (%s)\n", block_index,
589                                 cl_ntoh64(osm_node_get_node_guid(p_node)),
590                                 osm_physp_get_port_num(p_physp),
591                                 p_physp->p_node->print_desc);
592                 else {
593                         OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0506: "
594                                 "pkey_mgr_update_pkey_entry() failed to update "
595                                 "pkey table block %u for node 0x%016" PRIx64
596                                 " port %u (%s)\n", block_index,
597                                 cl_ntoh64(osm_node_get_node_guid(p_node)),
598                                 osm_physp_get_port_num(p_physp),
599                                 p_physp->p_node->print_desc);
600                         ret = -1;
601                 }
602         }
603
604         return ret;
605 }
606
607 static int last_used_pkey_index(const osm_port_t * const p_port,
608                                 const osm_pkey_tbl_t * p_pkey_tbl,
609                                 uint16_t * p_last_index)
610 {
611         ib_pkey_table_t *last_block;
612         uint16_t index, last_index = 0;
613
614         CL_ASSERT(p_last_index);
615
616         last_block = osm_pkey_tbl_new_block_get(p_pkey_tbl,
617                                                 p_pkey_tbl->used_blocks - 1);
618         if (!last_block)
619                 return 1;
620
621         if (p_pkey_tbl->used_blocks == p_pkey_tbl->max_blocks)
622                 last_index = cl_ntoh16(p_port->p_node->node_info.partition_cap) % IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
623         if (last_index == 0)
624                 last_index = IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
625         index = last_index;
626         do {
627                 index--;
628                 if (!ib_pkey_is_invalid(last_block->pkey_entry[index]))
629                         break;
630         } while (index != 0);
631
632         *p_last_index = index;
633         return 0;
634 }
635
636 static int update_peer_block(osm_log_t * p_log, osm_sm_t * sm,
637                              osm_physp_t * peer,
638                              osm_pkey_tbl_t * p_peer_pkey_tbl,
639                              ib_pkey_table_t * new_peer_block,
640                              uint16_t peer_block_idx, osm_node_t * p_node)
641 {
642         int ret = 0;
643         ib_pkey_table_t *peer_block;
644
645         peer_block = osm_pkey_tbl_block_get(p_peer_pkey_tbl, peer_block_idx);
646         if (!peer_block ||
647             memcmp(peer_block, new_peer_block, sizeof(*peer_block))) {
648                 if (pkey_mgr_update_pkey_entry(sm, peer, new_peer_block,
649                                                peer_block_idx) != IB_SUCCESS) {
650                         OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0509: "
651                                 "pkey_mgr_update_pkey_entry() failed to update "
652                                 "pkey table block %u for node 0x%016"
653                                 PRIx64 " port %u (%s)\n",
654                                 peer_block_idx,
655                                 cl_ntoh64(osm_node_get_node_guid(p_node)),
656                                 osm_physp_get_port_num(peer),
657                                 p_node->print_desc);
658                         ret = -1;
659                 }
660         }
661
662         return ret;
663 }
664
665 static int new_pkey_exists(osm_pkey_tbl_t * p_pkey_tbl, ib_net16_t pkey)
666 {
667         uint16_t num_blocks;
668         uint16_t block_index;
669         ib_pkey_table_t *block;
670         uint16_t pkey_idx;
671
672         num_blocks = (uint16_t) cl_ptr_vector_get_size(&p_pkey_tbl->new_blocks);
673         for (block_index = 0; block_index < num_blocks; block_index++) {
674                 block = osm_pkey_tbl_new_block_get(p_pkey_tbl, block_index);
675                 if (!block)
676                         continue;
677
678                 for (pkey_idx = 0; pkey_idx < IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
679                      pkey_idx++) {
680                         if (block->pkey_entry[pkey_idx] == pkey)
681                                 return 1;
682                 }
683         }
684         return 0;
685 }
686
687 static int pkey_mgr_update_peer_port(osm_log_t * p_log, osm_sm_t * sm,
688                                      const osm_subn_t * p_subn,
689                                      const osm_port_t * const p_port,
690                                      osm_partition_enforce_type_enum enforce_type)
691 {
692         osm_physp_t *p_physp, *peer;
693         osm_node_t *p_node;
694         ib_pkey_table_t *block;
695         const osm_pkey_tbl_t *p_pkey_tbl;
696         osm_pkey_tbl_t *p_peer_pkey_tbl;
697         uint16_t block_index, peer_block_idx;
698         uint16_t peer_max_blocks;
699         uint16_t last_index;
700         ib_pkey_table_t new_peer_block;
701         uint16_t pkey_idx, peer_pkey_idx;
702         ib_net16_t pkey, full_pkey;
703         int ret = 0, loop_exit = 0;
704
705         p_physp = p_port->p_physp;
706         if (!p_physp)
707                 return -1;
708         peer = osm_physp_get_remote(p_physp);
709         if (!peer)
710                 return -1;
711         p_node = osm_physp_get_node_ptr(peer);
712         if (!p_node->sw || !p_node->sw->switch_info.enforce_cap)
713                 return 0;
714
715         if (enforce_type == OSM_PARTITION_ENFORCE_TYPE_OFF) {
716                 pkey_mgr_enforce_partition(p_log, sm, peer, OSM_PARTITION_ENFORCE_TYPE_OFF);
717                 return ret;
718         }
719
720         p_pkey_tbl = osm_physp_get_pkey_tbl(p_physp);
721         peer_max_blocks = pkey_mgr_get_physp_max_blocks(peer);
722         p_peer_pkey_tbl = &peer->pkeys;
723         peer_block_idx = 0;
724         peer_pkey_idx = 0;
725         for (block_index = 0; block_index < p_pkey_tbl->used_blocks;
726              block_index++) {
727                 if (loop_exit)
728                         break;
729                 block = osm_pkey_tbl_new_block_get(p_pkey_tbl, block_index);
730                 if (!block)
731                         continue;
732                 for (pkey_idx = 0; pkey_idx < IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
733                      pkey_idx++) {
734                         pkey = block->pkey_entry[pkey_idx];
735                         if (ib_pkey_is_invalid(pkey))
736                                 continue;
737                         if (!ib_pkey_is_full_member(pkey)) {
738                                 full_pkey = pkey | IB_PKEY_TYPE_MASK;
739                                 if (new_pkey_exists(&p_physp->pkeys, full_pkey))
740                                         continue;
741                         }
742                         new_peer_block.pkey_entry[peer_pkey_idx] = pkey;
743                         if (peer_block_idx >= peer_max_blocks) {
744                                 loop_exit = 1;
745                                 break;
746                         }
747                         if (++peer_pkey_idx == IB_NUM_PKEY_ELEMENTS_IN_BLOCK) {
748                                 if (update_peer_block(p_log, sm, peer,
749                                                       p_peer_pkey_tbl,
750                                                       &new_peer_block,
751                                                       peer_block_idx, p_node))
752                                         ret = -1;
753                                 peer_pkey_idx = 0;
754                                 peer_block_idx++;
755                         }
756                 }
757         }
758
759         if (peer_block_idx < peer_max_blocks) {
760                 if (peer_pkey_idx) {
761                         /* Handle partial last block */
762                         for (; peer_pkey_idx < IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
763                              peer_pkey_idx++)
764                                 new_peer_block.pkey_entry[peer_pkey_idx] = 0;
765                         if (update_peer_block(p_log, sm, peer, p_peer_pkey_tbl,
766                                               &new_peer_block, peer_block_idx,
767                                               p_node))
768                                 ret = -1;
769                 } else
770                         peer_block_idx--;
771
772                 p_peer_pkey_tbl->used_blocks = peer_block_idx + 1;
773                 if (p_peer_pkey_tbl->used_blocks == peer_max_blocks) {
774                         /* Is last used pkey index beyond switch peer port capacity ? */
775                         if (!last_used_pkey_index(p_port, p_peer_pkey_tbl,
776                                                   &last_index)) {
777                                 last_index += peer_block_idx * IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
778                                 if (cl_ntoh16(p_node->sw->switch_info.enforce_cap) <= last_index) {
779                                         OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0507: "
780                                                 "Not enough pkey entries (%u <= %u) on switch 0x%016"
781                                                 PRIx64 " port %u (%s). Clearing Enforcement bit\n",
782                                                 cl_ntoh16(p_node->sw->switch_info.enforce_cap),
783                                                 last_index,
784                                                 cl_ntoh64(osm_node_get_node_guid(p_node)),
785                                                 osm_physp_get_port_num(peer),
786                                                 p_node->print_desc);
787                                         enforce_type = OSM_PARTITION_ENFORCE_TYPE_OFF;
788                                         ret = -1;
789                                 }
790                         }
791                 }
792         } else {
793                 p_peer_pkey_tbl->used_blocks = peer_max_blocks;
794                 enforce_type = OSM_PARTITION_ENFORCE_TYPE_OFF;
795         }
796
797         if (!ret)
798                 OSM_LOG(p_log, OSM_LOG_DEBUG,
799                         "Pkey table was successfully updated for node 0x%016"
800                         PRIx64 " port %u (%s)\n",
801                         cl_ntoh64(osm_node_get_node_guid(p_node)),
802                         osm_physp_get_port_num(peer), p_node->print_desc);
803
804         if (pkey_mgr_enforce_partition(p_log, sm, peer, enforce_type))
805                 ret = -1;
806
807         return ret;
808 }
809
810 int osm_pkey_mgr_process(IN osm_opensm_t * p_osm)
811 {
812         cl_qmap_t *p_tbl;
813         cl_map_item_t *p_next;
814         osm_prtn_t *p_prtn;
815         osm_port_t *p_port;
816         osm_switch_t *p_sw;
817         osm_physp_t *p_physp;
818         osm_node_t *p_remote_node;
819         uint8_t i;
820         int ret = 0;
821
822         CL_ASSERT(p_osm);
823
824         OSM_LOG_ENTER(&p_osm->log);
825
826         CL_PLOCK_EXCL_ACQUIRE(&p_osm->lock);
827
828         if (osm_prtn_make_partitions(&p_osm->log, &p_osm->subn) != IB_SUCCESS) {
829                 OSM_LOG(&p_osm->log, OSM_LOG_ERROR, "ERR 0510: "
830                         "osm_prtn_make_partitions() failed\n");
831                 ret = -1;
832                 goto _err;
833         }
834
835         /* populate the pending pkey entries by scanning all partitions */
836         p_tbl = &p_osm->subn.prtn_pkey_tbl;
837         p_next = cl_qmap_head(p_tbl);
838         while (p_next != cl_qmap_end(p_tbl)) {
839                 p_prtn = (osm_prtn_t *) p_next;
840                 p_next = cl_qmap_next(p_next);
841                 pkey_mgr_process_partition_table(&p_osm->log, &p_osm->sm,
842                                                  p_prtn, FALSE);
843                 pkey_mgr_process_partition_table(&p_osm->log, &p_osm->sm,
844                                                  p_prtn, TRUE);
845         }
846
847         /* calculate and set new pkey tables */
848         p_tbl = &p_osm->subn.port_guid_tbl;
849         p_next = cl_qmap_head(p_tbl);
850         while (p_next != cl_qmap_end(p_tbl)) {
851                 p_port = (osm_port_t *) p_next;
852                 p_next = cl_qmap_next(p_next);
853                 if (pkey_mgr_update_port(&p_osm->log, &p_osm->sm, p_port))
854                         ret = -1;
855                 if ((osm_node_get_type(p_port->p_node) != IB_NODE_TYPE_SWITCH)
856                     && pkey_mgr_update_peer_port(&p_osm->log, &p_osm->sm,
857                                                  &p_osm->subn, p_port,
858                                                  p_osm->subn.opt.part_enforce_enum))
859                         ret = -1;
860         }
861
862         /* clear partition enforcement on inter-switch links */
863         p_tbl = &p_osm->subn.sw_guid_tbl;
864         p_next = cl_qmap_head(p_tbl);
865         while (p_next != cl_qmap_end(p_tbl)) {
866                 p_sw = (osm_switch_t *) p_next;
867                 p_next = cl_qmap_next(p_next);
868                 for (i = 1; i < p_sw->num_ports; i++) {
869                         p_physp = osm_node_get_physp_ptr(p_sw->p_node, i);
870                         if (p_physp && p_physp->p_remote_physp)
871                                 p_remote_node = p_physp->p_remote_physp->p_node;
872                         else
873                                 continue;
874
875                         if (osm_node_get_type(p_remote_node) != IB_NODE_TYPE_SWITCH)
876                                 continue;
877
878                         if(! (p_physp->port_info.vl_enforce & 0xc ))
879                                 continue;
880
881                         /* clear partition enforcement */
882                         if (pkey_mgr_enforce_partition(&p_osm->log, &p_osm->sm, p_physp, OSM_PARTITION_ENFORCE_TYPE_OFF))
883                                 ret = -1;
884                 }
885         }
886 _err:
887         CL_PLOCK_RELEASE(&p_osm->lock);
888         OSM_LOG_EXIT(&p_osm->log);
889         return ret;
890 }