]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ofed/opensm/opensm/osm_sa_link_record.c
MFV r347989:
[FreeBSD/FreeBSD.git] / contrib / ofed / opensm / opensm / osm_sa_link_record.c
1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2007 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  * Copyright (c) 2013 Oracle and/or its affiliates. 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 osm_lr_rcv_t.
40  * This object represents the LinkRecord Receiver object.
41  * This object is part of the opensm family of objects.
42  */
43
44 #if HAVE_CONFIG_H
45 #  include <config.h>
46 #endif                          /* HAVE_CONFIG_H */
47
48 #include <string.h>
49 #include <iba/ib_types.h>
50 #include <complib/cl_qmap.h>
51 #include <complib/cl_debug.h>
52 #include <opensm/osm_file_ids.h>
53 #define FILE_ID OSM_FILE_SA_LINK_RECORD_C
54 #include <vendor/osm_vendor_api.h>
55 #include <opensm/osm_node.h>
56 #include <opensm/osm_switch.h>
57 #include <opensm/osm_helper.h>
58 #include <opensm/osm_pkey.h>
59 #include <opensm/osm_sa.h>
60
61 #define SA_LR_RESP_SIZE SA_ITEM_RESP_SIZE(link_rec)
62
63 static void lr_rcv_build_physp_link(IN osm_sa_t * sa, IN ib_net16_t from_lid,
64                                     IN ib_net16_t to_lid, IN uint8_t from_port,
65                                     IN uint8_t to_port, IN cl_qlist_t * p_list)
66 {
67         osm_sa_item_t *p_lr_item;
68
69         p_lr_item = malloc(SA_LR_RESP_SIZE);
70         if (p_lr_item == NULL) {
71                 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1801: "
72                         "Unable to acquire link record\n"
73                         "\t\t\t\tFrom port %u\n" "\t\t\t\tTo port   %u\n"
74                         "\t\t\t\tFrom lid  %u\n" "\t\t\t\tTo lid    %u\n",
75                         from_port, to_port,
76                         cl_ntoh16(from_lid), cl_ntoh16(to_lid));
77                 return;
78         }
79         memset(p_lr_item, 0, SA_LR_RESP_SIZE);
80
81         p_lr_item->resp.link_rec.from_port_num = from_port;
82         p_lr_item->resp.link_rec.to_port_num = to_port;
83         p_lr_item->resp.link_rec.to_lid = to_lid;
84         p_lr_item->resp.link_rec.from_lid = from_lid;
85
86         cl_qlist_insert_tail(p_list, &p_lr_item->list_item);
87 }
88
89 static ib_net16_t get_base_lid(IN const osm_physp_t * p_physp)
90 {
91         if (p_physp->p_node->node_info.node_type == IB_NODE_TYPE_SWITCH)
92                 p_physp = osm_node_get_physp_ptr(p_physp->p_node, 0);
93         return osm_physp_get_base_lid(p_physp);
94 }
95
96 static void lr_rcv_get_physp_link(IN osm_sa_t * sa,
97                                   IN const ib_link_record_t * p_lr,
98                                   IN const osm_physp_t * p_src_physp,
99                                   IN const osm_physp_t * p_dest_physp,
100                                   IN const ib_net64_t comp_mask,
101                                   IN cl_qlist_t * p_list,
102                                   IN const osm_physp_t * p_req_physp)
103 {
104         uint8_t src_port_num;
105         uint8_t dest_port_num;
106         ib_net16_t from_base_lid;
107         ib_net16_t to_base_lid;
108         ib_net16_t lmc_mask;
109
110         OSM_LOG_ENTER(sa->p_log);
111
112         /*
113            If only one end of the link is specified, determine
114            the other side.
115          */
116         if (p_src_physp) {
117                 if (p_dest_physp) {
118                         /*
119                            Ensure the two physp's are actually connected.
120                            If not, bail out.
121                          */
122                         if (osm_physp_get_remote(p_src_physp) != p_dest_physp)
123                                 goto Exit;
124                 } else {
125                         p_dest_physp = osm_physp_get_remote(p_src_physp);
126                         if (p_dest_physp == NULL)
127                                 goto Exit;
128                 }
129         } else {
130                 if (p_dest_physp) {
131                         p_src_physp = osm_physp_get_remote(p_dest_physp);
132                         if (p_src_physp == NULL)
133                                 goto Exit;
134                 } else
135                         goto Exit;      /* no physp's, so nothing to do */
136         }
137
138         /* Check that the p_src_physp, p_dest_physp and p_req_physp
139            all share a pkey (doesn't have to be the same p_key). */
140         if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_dest_physp,
141                                   sa->p_subn->opt.allow_both_pkeys)) {
142                 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
143                         "Source and Dest PhysPorts do not share PKey\n");
144                 goto Exit;
145         }
146         if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_req_physp,
147                                   sa->p_subn->opt.allow_both_pkeys)) {
148                 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
149                         "Source and Requester PhysPorts do not share PKey\n");
150                 goto Exit;
151         }
152         if (!osm_physp_share_pkey(sa->p_log, p_req_physp, p_dest_physp,
153                                   sa->p_subn->opt.allow_both_pkeys)) {
154                 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
155                         "Requester and Dest PhysPorts do not share PKey\n");
156                 goto Exit;
157         }
158
159         src_port_num = osm_physp_get_port_num(p_src_physp);
160         dest_port_num = osm_physp_get_port_num(p_dest_physp);
161
162         if (comp_mask & IB_LR_COMPMASK_FROM_PORT)
163                 if (src_port_num != p_lr->from_port_num)
164                         goto Exit;
165
166         if (comp_mask & IB_LR_COMPMASK_TO_PORT)
167                 if (dest_port_num != p_lr->to_port_num)
168                         goto Exit;
169
170         from_base_lid = get_base_lid(p_src_physp);
171         to_base_lid = get_base_lid(p_dest_physp);
172
173         lmc_mask = ~((1 << sa->p_subn->opt.lmc) - 1);
174         lmc_mask = cl_hton16(lmc_mask);
175
176         if (comp_mask & IB_LR_COMPMASK_FROM_LID)
177                 if (from_base_lid != (p_lr->from_lid & lmc_mask))
178                         goto Exit;
179
180         if (comp_mask & IB_LR_COMPMASK_TO_LID)
181                 if (to_base_lid != (p_lr->to_lid & lmc_mask))
182                         goto Exit;
183
184         OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Acquiring link record\n"
185                 "\t\t\t\tsrc port 0x%" PRIx64 " (port %u)"
186                 ", dest port 0x%" PRIx64 " (port %u)\n",
187                 cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), src_port_num,
188                 cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)),
189                 dest_port_num);
190
191         lr_rcv_build_physp_link(sa, from_base_lid, to_base_lid, src_port_num,
192                                 dest_port_num, p_list);
193
194 Exit:
195         OSM_LOG_EXIT(sa->p_log);
196 }
197
198 static void lr_rcv_get_port_links(IN osm_sa_t * sa,
199                                   IN const ib_link_record_t * p_lr,
200                                   IN const osm_port_t * p_src_port,
201                                   IN const osm_port_t * p_dest_port,
202                                   IN const ib_net64_t comp_mask,
203                                   IN cl_qlist_t * p_list,
204                                   IN const osm_physp_t * p_req_physp)
205 {
206         const osm_physp_t *p_src_physp;
207         const osm_physp_t *p_dest_physp;
208         const cl_qmap_t *p_node_tbl;
209         osm_node_t *p_node;
210         uint8_t port_num;
211         uint8_t num_ports;
212         uint8_t dest_num_ports;
213         uint8_t dest_port_num;
214
215         OSM_LOG_ENTER(sa->p_log);
216
217         if (p_src_port) {
218                 if (p_dest_port) {
219                         /*
220                            Build an LR for every link connected between both ports.
221                            The inner function will discard physp combinations
222                            that do not actually connect.  Don't bother screening
223                            for that here.
224                          */
225                         num_ports = osm_node_get_num_physp(p_src_port->p_node);
226                         dest_num_ports =
227                             osm_node_get_num_physp(p_dest_port->p_node);
228                         for (port_num = 1; port_num < num_ports; port_num++) {
229                                 p_src_physp =
230                                     osm_node_get_physp_ptr(p_src_port->p_node,
231                                                            port_num);
232                                 for (dest_port_num = 1;
233                                      dest_port_num < dest_num_ports;
234                                      dest_port_num++) {
235                                         p_dest_physp =
236                                             osm_node_get_physp_ptr(p_dest_port->
237                                                                    p_node,
238                                                                    dest_port_num);
239                                         /* both physical ports should be with data */
240                                         if (p_src_physp && p_dest_physp)
241                                                 lr_rcv_get_physp_link
242                                                     (sa, p_lr, p_src_physp,
243                                                      p_dest_physp, comp_mask,
244                                                      p_list, p_req_physp);
245                                 }
246                         }
247                 } else {
248                         /*
249                            Build an LR for every link connected from the source port.
250                          */
251                         if (comp_mask & IB_LR_COMPMASK_FROM_PORT) {
252                                 port_num = p_lr->from_port_num;
253                                 /* If the port number is out of the range of the p_src_port, then
254                                    this couldn't be a relevant record. */
255                                 if (port_num <
256                                     p_src_port->p_node->physp_tbl_size) {
257                                         p_src_physp =
258                                             osm_node_get_physp_ptr(p_src_port->
259                                                                    p_node,
260                                                                    port_num);
261                                         if (p_src_physp)
262                                                 lr_rcv_get_physp_link
263                                                     (sa, p_lr, p_src_physp,
264                                                      NULL, comp_mask, p_list,
265                                                      p_req_physp);
266                                 }
267                         } else {
268                                 num_ports =
269                                     osm_node_get_num_physp(p_src_port->p_node);
270                                 for (port_num = 1; port_num < num_ports;
271                                      port_num++) {
272                                         p_src_physp =
273                                             osm_node_get_physp_ptr(p_src_port->
274                                                                    p_node,
275                                                                    port_num);
276                                         if (p_src_physp)
277                                                 lr_rcv_get_physp_link
278                                                     (sa, p_lr, p_src_physp,
279                                                      NULL, comp_mask, p_list,
280                                                      p_req_physp);
281                                 }
282                         }
283                 }
284         } else {
285                 if (p_dest_port) {
286                         /*
287                            Build an LR for every link connected to the dest port.
288                          */
289                         if (comp_mask & IB_LR_COMPMASK_TO_PORT) {
290                                 port_num = p_lr->to_port_num;
291                                 /* If the port number is out of the range of the p_dest_port, then
292                                    this couldn't be a relevant record. */
293                                 if (port_num <
294                                     p_dest_port->p_node->physp_tbl_size) {
295                                         p_dest_physp =
296                                             osm_node_get_physp_ptr(p_dest_port->
297                                                                    p_node,
298                                                                    port_num);
299                                         if (p_dest_physp)
300                                                 lr_rcv_get_physp_link
301                                                     (sa, p_lr, NULL,
302                                                      p_dest_physp, comp_mask,
303                                                      p_list, p_req_physp);
304                                 }
305                         } else {
306                                 num_ports =
307                                     osm_node_get_num_physp(p_dest_port->p_node);
308                                 for (port_num = 1; port_num < num_ports;
309                                      port_num++) {
310                                         p_dest_physp =
311                                             osm_node_get_physp_ptr(p_dest_port->
312                                                                    p_node,
313                                                                    port_num);
314                                         if (p_dest_physp)
315                                                 lr_rcv_get_physp_link
316                                                     (sa, p_lr, NULL,
317                                                      p_dest_physp, comp_mask,
318                                                      p_list, p_req_physp);
319                                 }
320                         }
321                 } else {
322                         /*
323                            Process the world (recurse once back into this function).
324                          */
325                         p_node_tbl = &sa->p_subn->node_guid_tbl;
326                         p_node = (osm_node_t *) cl_qmap_head(p_node_tbl);
327
328                         while (p_node != (osm_node_t *) cl_qmap_end(p_node_tbl)) {
329                                 num_ports = osm_node_get_num_physp(p_node);
330                                 for (port_num = 1; port_num < num_ports;
331                                      port_num++) {
332                                         p_src_physp =
333                                             osm_node_get_physp_ptr(p_node,
334                                                                    port_num);
335                                         if (p_src_physp)
336                                                 lr_rcv_get_physp_link
337                                                     (sa, p_lr, p_src_physp,
338                                                      NULL, comp_mask, p_list,
339                                                      p_req_physp);
340                                 }
341                                 p_node = (osm_node_t *) cl_qmap_next(&p_node->
342                                                                      map_item);
343                         }
344                 }
345         }
346
347         OSM_LOG_EXIT(sa->p_log);
348 }
349
350 /**********************************************************************
351  Returns the SA status to return to the client.
352  **********************************************************************/
353 static ib_net16_t lr_rcv_get_end_points(IN osm_sa_t * sa,
354                                         IN const osm_madw_t * p_madw,
355                                         OUT const osm_port_t ** pp_src_port,
356                                         OUT const osm_port_t ** pp_dest_port)
357 {
358         const ib_link_record_t *p_lr;
359         const ib_sa_mad_t *p_sa_mad;
360         ib_net64_t comp_mask;
361         ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS;
362
363         OSM_LOG_ENTER(sa->p_log);
364
365         /*
366            Determine what fields are valid and then get a pointer
367            to the source and destination port objects, if possible.
368          */
369         p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
370         p_lr = (ib_link_record_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
371
372         comp_mask = p_sa_mad->comp_mask;
373         *pp_src_port = NULL;
374         *pp_dest_port = NULL;
375
376         if (comp_mask & IB_LR_COMPMASK_FROM_LID) {
377                 *pp_src_port = osm_get_port_by_lid(sa->p_subn, p_lr->from_lid);
378                 if (!*pp_src_port) {
379                         /*
380                            This 'error' is the client's fault (bad lid) so
381                            don't enter it as an error in our own log.
382                            Return an error response to the client.
383                          */
384                         OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
385                                 "No source port with LID %u\n",
386                                 cl_ntoh16(p_lr->from_lid));
387
388                         sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
389                         goto Exit;
390                 }
391         }
392
393         if (comp_mask & IB_LR_COMPMASK_TO_LID) {
394                 *pp_dest_port = osm_get_port_by_lid(sa->p_subn, p_lr->to_lid);
395                 if (!*pp_dest_port) {
396                         /*
397                            This 'error' is the client's fault (bad lid) so
398                            don't enter it as an error in our own log.
399                            Return an error response to the client.
400                          */
401                         OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
402                                 "No dest port with LID %u\n",
403                                 cl_ntoh16(p_lr->to_lid));
404
405                         sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
406                         goto Exit;
407                 }
408         }
409
410 Exit:
411         OSM_LOG_EXIT(sa->p_log);
412         return sa_status;
413 }
414
415 void osm_lr_rcv_process(IN void *context, IN void *data)
416 {
417         osm_sa_t *sa = context;
418         osm_madw_t *p_madw = data;
419         const ib_link_record_t *p_lr;
420         const ib_sa_mad_t *p_sa_mad;
421         const osm_port_t *p_src_port;
422         const osm_port_t *p_dest_port;
423         cl_qlist_t lr_list;
424         ib_net16_t status;
425         osm_physp_t *p_req_physp;
426
427         OSM_LOG_ENTER(sa->p_log);
428
429         CL_ASSERT(p_madw);
430
431         p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
432         p_lr = ib_sa_mad_get_payload_ptr(p_sa_mad);
433
434         CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_LINK_RECORD);
435
436         /* we only support SubnAdmGet and SubnAdmGetTable methods */
437         if (p_sa_mad->method != IB_MAD_METHOD_GET &&
438             p_sa_mad->method != IB_MAD_METHOD_GETTABLE) {
439                 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1804: "
440                         "Unsupported Method (%s) for LinkRecord request\n",
441                         ib_get_sa_method_str(p_sa_mad->method));
442                 osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
443                 goto Exit;
444         }
445
446         cl_plock_acquire(sa->p_lock);
447
448         /* update the requester physical port */
449         p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
450                                                 osm_madw_get_mad_addr_ptr
451                                                 (p_madw));
452         if (p_req_physp == NULL) {
453                 cl_plock_release(sa->p_lock);
454                 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1805: "
455                         "Cannot find requester physical port\n");
456                 goto Exit;
457         }
458
459         if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
460                 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
461                         "Requester port GUID 0x%" PRIx64 "\n",
462                         cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
463                 osm_dump_link_record_v2(sa->p_log, p_lr, FILE_ID, OSM_LOG_DEBUG);
464         }
465
466         cl_qlist_init(&lr_list);
467
468         /*
469            Most SA functions (including this one) are read-only on the
470            subnet object, so we grab the lock non-exclusively.
471          */
472         status = lr_rcv_get_end_points(sa, p_madw, &p_src_port, &p_dest_port);
473
474         if (status == IB_SA_MAD_STATUS_SUCCESS)
475                 lr_rcv_get_port_links(sa, p_lr, p_src_port, p_dest_port,
476                                       p_sa_mad->comp_mask, &lr_list,
477                                       p_req_physp);
478
479         cl_plock_release(sa->p_lock);
480
481         osm_sa_respond(sa, p_madw, sizeof(ib_link_record_t), &lr_list);
482
483 Exit:
484         OSM_LOG_EXIT(sa->p_log);
485 }