[OpenSM] -
[mirror/winof/.git] / ulp / opensm / user / opensm / osm_sa_mcmember_record.c
1 /*
2  * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under the OpenIB.org BSD license
7  * below:
8  *
9  *     Redistribution and use in source and binary forms, with or
10  *     without modification, are permitted provided that the following
11  *     conditions are met:
12  *
13  *      - Redistributions of source code must retain the above
14  *        copyright notice, this list of conditions and the following
15  *        disclaimer.
16  *
17  *      - Redistributions in binary form must reproduce the above
18  *        copyright notice, this list of conditions and the following
19  *        disclaimer in the documentation and/or other materials
20  *        provided with the distribution.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
26  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29  * SOFTWARE.
30  *
31  * $Id$
32  */
33
34
35 /*
36  * Abstract:
37  *    Implementation of osm_mcmr_recv_t.
38  * This object represents the MCMemberRecord Receiver object.
39  * This object is part of the opensm family of objects.
40  *
41  * Environment:
42  *    Linux User Mode
43  *
44  * $Revision: 1.15 $
45  */
46
47 /*
48   Next available error code: 0x403
49 */
50
51 #if HAVE_CONFIG_H
52 #  include <config.h>
53 #endif /* HAVE_CONFIG_H */
54
55 #include <iba/ib_types.h>
56 #include <complib/cl_memory.h>
57 #include <complib/cl_qmap.h>
58 #include <complib/cl_passivelock.h>
59 #include <complib/cl_debug.h>
60 #include <complib/cl_qlist.h>
61 #include <opensm/osm_sa_mcmember_record.h>
62 #include <opensm/osm_sa_response.h>
63 #include <opensm/osm_madw.h>
64 #include <opensm/osm_log.h>
65 #include <opensm/osm_subnet.h>
66 #include <opensm/osm_mad_pool.h>
67 #include <vendor/osm_vendor.h>
68 #include <vendor/osm_vendor_api.h>
69 #include <opensm/osm_helper.h>
70 #include <opensm/osm_msgdef.h>
71 #include <opensm/osm_pkey.h>
72 #include <opensm/osm_inform.h>
73
74 #define OSM_MCMR_RCV_POOL_MIN_SIZE     32
75 #define OSM_MCMR_RCV_POOL_GROW_SIZE    32
76
77 typedef  struct _osm_mcmr_item
78 {
79   cl_pool_item_t        pool_item;
80   ib_member_rec_t    rec;
81
82 } osm_mcmr_item_t;
83
84 typedef  struct   osm_sa_mcmr_search_ctxt {
85   const ib_member_rec_t  *p_mcmember_rec;
86   osm_mgrp_t      *p_mgrp;
87   osm_mcmr_recv_t *p_rcv;
88   cl_qlist_t      *p_list; /*  hold results */
89   ib_net64_t      comp_mask;
90   const osm_physp_t*    p_req_physp;
91 } osm_sa_mcmr_search_ctxt_t;
92
93 /**********************************************************************
94  **********************************************************************/
95 void
96 osm_mcmr_rcv_construct(
97   IN osm_mcmr_recv_t* const p_rcv )
98 {
99   cl_memclr( p_rcv, sizeof(*p_rcv) );
100   cl_qlock_pool_construct( &p_rcv->pool );
101 }
102
103 /**********************************************************************
104  **********************************************************************/
105 void
106 osm_mcmr_rcv_destroy(
107   IN osm_mcmr_recv_t* const p_rcv )
108 {
109   CL_ASSERT( p_rcv );
110
111   OSM_LOG_ENTER( p_rcv->p_log, osm_mcmr_rcv_destroy );
112
113   cl_qlock_pool_destroy( &p_rcv->pool );
114
115   OSM_LOG_EXIT( p_rcv->p_log );
116 }
117
118 /**********************************************************************
119  **********************************************************************/
120 ib_api_status_t
121 osm_mcmr_rcv_init(
122   IN osm_sm_t * const p_sm,
123   IN osm_mcmr_recv_t* const p_rcv,
124   IN osm_sa_resp_t* const p_resp,
125   IN osm_mad_pool_t* const p_mad_pool,
126   IN osm_subn_t* const p_subn,
127   IN osm_log_t* const p_log,
128   IN cl_plock_t* const p_lock )
129 {
130   ib_api_status_t status = IB_SUCCESS;
131   OSM_LOG_ENTER( p_log, osm_mcmr_rcv_init );
132
133   osm_mcmr_rcv_construct( p_rcv );
134
135   p_rcv->p_log = p_log;
136   p_rcv->p_subn = p_subn;
137   p_rcv->p_sm = p_sm;
138   p_rcv->p_lock = p_lock;
139   p_rcv->p_resp = p_resp;
140   p_rcv->p_mad_pool = p_mad_pool;
141   p_rcv->mlid_ho = 0xC000;
142
143   status = cl_qlock_pool_init( &p_rcv->pool,
144                                OSM_MCMR_RCV_POOL_MIN_SIZE,
145                                0,
146                                OSM_MCMR_RCV_POOL_GROW_SIZE,
147                                sizeof(osm_mcmr_item_t),
148                                NULL, NULL, NULL );
149   if (status != CL_SUCCESS)
150   {
151     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
152              "osm_mcmr_rcv_init: ERR 1B02: "
153              "Error Init of qlock pool (%d)\n",
154              status  );
155   }
156   OSM_LOG_EXIT( p_rcv->p_log );
157   return( status );
158 }
159
160 /**********************************************************************
161  A search function that compares the given mgrp with the search context
162  if there is a match by mgid the p_mgrp is copied to the search context
163  p_mgrp component
164
165  Inputs:
166  p_map_item - which is part of a mgrp object
167  context - points to the osm_sa_mcmr_search_ctxt_t including the mgid
168    looked for and the result p_mgrp
169 **********************************************************************/
170 static
171 void
172 __search_mgrp_by_mgid(
173   IN  cl_map_item_t* const    p_map_item,
174   IN  void*                   context )
175 {
176   osm_mgrp_t* p_mgrp = (osm_mgrp_t*)p_map_item;
177   osm_sa_mcmr_search_ctxt_t *p_ctxt = (osm_sa_mcmr_search_ctxt_t *) context;
178   const ib_member_rec_t        *p_recvd_mcmember_rec;
179   osm_mcmr_recv_t *p_rcv;
180
181   p_recvd_mcmember_rec = p_ctxt->p_mcmember_rec;
182   p_rcv = p_ctxt->p_rcv;
183
184   /* ignore groups marked for deletion */
185   if (p_mgrp->to_be_deleted)
186     return;
187  
188   /* compare entire MGID so different scope will not sneak in for
189      the same MGID */
190   if (cl_memcmp(&p_mgrp->mcmember_rec.mgid,
191                 &p_recvd_mcmember_rec->mgid,
192                 sizeof(ib_gid_t)))
193     return;
194
195   if(p_ctxt->p_mgrp)
196   {
197     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
198              "__search_mgrp_by_mgid: ERR 1B03: "
199              "Multiple MC groups for same MGID\n" );
200     return;
201   }
202   p_ctxt->p_mgrp = p_mgrp;
203
204 }
205
206 /**********************************************************************
207  Look for a MGRP in the mgrp_mlid_tbl by mlid
208 **********************************************************************/
209 static
210 osm_mgrp_t *
211 __get_mgrp_by_mlid(
212   IN osm_mcmr_recv_t* const p_rcv,
213   IN ib_net16_t const mlid)
214 {
215   cl_map_item_t *map_item;
216
217   map_item = cl_qmap_get(&p_rcv->p_subn->mgrp_mlid_tbl,
218                          mlid);
219   if(map_item == cl_qmap_end(&p_rcv->p_subn->mgrp_mlid_tbl))
220   {
221     return NULL;
222   }
223   return (osm_mgrp_t *)map_item;
224
225 }
226
227 /**********************************************************************
228 Look for a MGRP in the mgrp_mlid_tbl by mgid
229 ***********************************************************************/
230 static ib_api_status_t
231 __get_mgrp_by_mgid(
232   IN osm_mcmr_recv_t* const p_rcv,
233   IN ib_member_rec_t* p_recvd_mcmember_rec,
234   OUT osm_mgrp_t **pp_mgrp)
235 {
236   osm_sa_mcmr_search_ctxt_t mcmr_search_context;
237
238   mcmr_search_context.p_mcmember_rec = p_recvd_mcmember_rec;
239   mcmr_search_context.p_rcv = p_rcv;
240   mcmr_search_context.p_mgrp = NULL;
241
242   cl_qmap_apply_func( &p_rcv->p_subn->mgrp_mlid_tbl,
243                       __search_mgrp_by_mgid,
244                       &mcmr_search_context);
245
246   if(mcmr_search_context.p_mgrp == NULL)
247   {
248     return IB_NOT_FOUND;
249   }
250
251   *pp_mgrp = mcmr_search_context.p_mgrp;
252   return IB_SUCCESS;
253 }
254
255 /*********************************************************************
256 copy certain fields between two mcmember records
257 used during the process of join request to copy data from the mgrp to the
258 port record.
259 **********************************************************************/
260 static inline void
261 __copy_from_create_mc_rec(
262   IN ib_member_rec_t * const dest,
263   IN const ib_member_rec_t *const src)
264 {
265   dest->qkey = src->qkey;
266   dest->mlid = src->mlid;
267   dest->tclass = src->tclass;
268   dest->pkey = src->pkey;
269   dest->sl_flow_hop = src->sl_flow_hop;
270   dest->mtu = src->mtu;
271   dest->rate = src->rate;
272   dest->pkt_life = src->pkt_life;
273 }
274
275 /*********************************************************************
276 Return an mlid to the pool of free mlids.
277 But this implementation is not a pool - it is simply scanning through
278 the MGRP database for unused mlids...
279 *********************************************************************/
280 static void
281 __free_mlid(
282   IN osm_mcmr_recv_t* const p_rcv,
283   IN uint16_t mlid)
284 {
285   UNUSED_PARAM(p_rcv);
286   UNUSED_PARAM(mlid);
287 }
288
289 /*********************************************************************
290 Get a new unused mlid by scanning all the used ones in the subnet.
291 TODO: Implement a more scalable - O(1) solution based on pool of
292 available mlids.
293 **********************************************************************/
294 static ib_net16_t
295 __get_new_mlid(
296   IN osm_mcmr_recv_t* const p_rcv)
297 {
298   osm_subn_t   *p_subn = p_rcv->p_subn;
299   osm_mgrp_t   *p_mgrp;
300   uint8_t      *used_mlids_array;
301   uint16_t      idx;
302   uint16_t      mlid; /* the result */
303   uint16_t      max_num_mlids;
304
305   OSM_LOG_ENTER(p_rcv->p_log, __get_new_mlid);
306  
307   /* If empty MCGroups table - at first return the min mlid */
308   p_mgrp = (osm_mgrp_t*)cl_qmap_head( &p_subn->mgrp_mlid_tbl );
309   if (p_mgrp == (osm_mgrp_t*)cl_qmap_end( &p_subn->mgrp_mlid_tbl ))
310   {
311     mlid = IB_LID_MCAST_START_HO;
312     osm_log( p_rcv->p_log, OSM_LOG_VERBOSE,
313              "__get_new_mlid: "
314              "No multicast groups found using minimal mlid:0x%04X\n",
315              mlid );
316     goto Exit;
317   }
318  
319   max_num_mlids =
320     p_rcv->p_subn->max_multicast_lid_ho - IB_LID_MCAST_START_HO;
321  
322   /* track all used mlids in the array (by mlid index) */
323   used_mlids_array =
324     (uint8_t *)cl_zalloc(sizeof(uint8_t)*max_num_mlids);
325  
326   /* scan all available multicast groups in the DB and fill in the table */
327   while( p_mgrp != (osm_mgrp_t*)cl_qmap_end( &p_subn->mgrp_mlid_tbl ) )
328   {
329     /* ignore mgrps marked for deletion */
330     if (p_mgrp->to_be_deleted == FALSE)
331     {
332       osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
333                "__get_new_mlid: "
334                "Found mgrp with lid:0x%X MGID: 0x%016" PRIx64 " : "
335                "0x%016" PRIx64 "\n",
336                cl_ntoh16( p_mgrp->mlid),
337                cl_ntoh64( p_mgrp->mcmember_rec.mgid.unicast.prefix ),
338                cl_ntoh64( p_mgrp->mcmember_rec.mgid.unicast.interface_id ) );
339       
340       /* Map in table */
341       if (cl_ntoh16(p_mgrp->mlid) > p_rcv->p_subn->max_multicast_lid_ho)
342       {
343         osm_log( p_rcv->p_log, OSM_LOG_ERROR,
344                  "__get_new_mlid: ERR 1B27: "
345                  "Found mgrp with mlid:0x%04X > max allowed mlid:0x%04X\n",
346                  cl_ntoh16(p_mgrp->mlid), 
347                  max_num_mlids + IB_LID_MCAST_START_HO);
348       }
349       else
350       {
351         used_mlids_array[cl_ntoh16(p_mgrp->mlid) - IB_LID_MCAST_START_HO] = 1;
352       }
353     }
354     p_mgrp = (osm_mgrp_t*)cl_qmap_next( &p_mgrp->map_item );
355   }
356  
357   /* Find "mlid holes" in the mgrp table */ 
358   for (idx = 0;
359        (idx < max_num_mlids) && (used_mlids_array[idx] == 1);
360        idx++);
361  
362   /* did it go above the maximal mlid allowed */
363   if ( idx < max_num_mlids )
364   {
365     mlid = idx + IB_LID_MCAST_START_HO;
366     osm_log( p_rcv->p_log, OSM_LOG_VERBOSE,
367              "__get_new_mlid: "
368              "Found available mlid:0x%04X at idx:%u\n",
369              mlid, idx);
370   }
371   else
372   {
373     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
374              "__get_new_mlid: ERR 1B23: "
375              "All available:%u mlids are taken\n",
376              max_num_mlids);   
377     mlid = 0;
378   }
379  
380   cl_free(used_mlids_array);
381  
382  Exit:
383   OSM_LOG_EXIT(p_rcv->p_log);
384   return cl_hton16(mlid);
385 }
386
387 /*********************************************************************
388 This procedure is only invoked to cleanup INTERMEDIATE mgrp.
389 If there is only one port on the mgrp it means that the current
390 request was the only member and the group is not really needed. So we
391 silently drop it. Since it was an intermediate group no need to
392 re-route it.
393 **********************************************************************/
394 static void
395 __cleanup_mgrp(
396   IN osm_mcmr_recv_t* const p_rcv,
397   IN ib_net16_t const mlid)
398 {
399   osm_mgrp_t *p_mgrp;
400
401   p_mgrp = __get_mgrp_by_mlid(p_rcv, mlid);
402   if(p_mgrp)
403   {
404     /* Remove MGRP only if osm_mcm_port_t count is 0 and
405      * Not a well known group
406      */
407     if(cl_is_qmap_empty(&p_mgrp->mcm_port_tbl) &&
408        (p_mgrp->well_known == FALSE))
409     {
410       cl_qmap_remove_item(&p_rcv->p_subn->mgrp_mlid_tbl,
411                           (cl_map_item_t *)p_mgrp );
412       osm_mgrp_destroy(p_mgrp);
413     }
414   }
415 }
416
417 /*********************************************************************
418 Add a port to the group. Calculating its PROXY_JOIN by the Port and
419 requester gids.
420 **********************************************************************/
421 static
422 ib_api_status_t
423 __add_new_mgrp_port(
424   IN osm_mcmr_recv_t * p_rcv,
425   IN osm_mgrp_t   *p_mgrp,
426   IN ib_member_rec_t *p_recvd_mcmember_rec,
427   IN osm_mad_addr_t        *p_mad_addr,
428   OUT osm_mcm_port_t **pp_mcmr_port)
429 {
430   boolean_t proxy_join;
431   ib_gid_t requester_gid;
432
433   /* set the proxy_join if the requester gid is not identical to the
434      joined gid */
435   requester_gid = osm_get_gid_by_mad_addr( p_rcv->p_log,
436                                            p_rcv->p_subn,
437                                            p_mad_addr );
438
439   if (! cl_memcmp(&p_recvd_mcmember_rec->port_gid, &requester_gid,
440                   sizeof(ib_gid_t)))
441   {
442     proxy_join = FALSE;
443     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
444              "__add_new_mgrp_port: "
445              "create new port with proxy_join FALSE\n");
446   }
447   else
448   {
449     /* The port is not the one specified in PortGID.
450        The check that the requester is in the same partition as
451        the PortGID is done before - just need to update the proxy_join. */
452     proxy_join = TRUE;
453     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
454              "__add_new_mgrp_port: "
455              "create new port with proxy_join TRUE\n");
456   }
457
458   *pp_mcmr_port = osm_mgrp_add_port(p_mgrp,
459                                     &p_recvd_mcmember_rec->port_gid,
460                                     p_recvd_mcmember_rec->scope_state,
461                                     proxy_join );
462   if(*pp_mcmr_port == NULL)
463   {
464     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
465              "__add_new_mgrp_port: ERR 1B06: "
466              "osm_mgrp_add_port failed\n");
467
468     return IB_INSUFFICIENT_MEMORY;
469   }
470
471   return IB_SUCCESS;
472 }
473
474 /**********************************************************************
475  **********************************************************************/
476 static inline boolean_t
477 __check_join_comp_mask(ib_net64_t comp_mask)
478 {
479   return( (comp_mask & JOIN_MC_COMP_MASK) == JOIN_MC_COMP_MASK);
480 }
481
482 /**********************************************************************
483  **********************************************************************/
484 static inline boolean_t
485 __check_create_comp_mask(ib_net64_t comp_mask,
486                          ib_member_rec_t *p_recvd_mcmember_rec)
487 {
488   return(
489     ((comp_mask & REQUIRED_MC_CREATE_COMP_MASK) == REQUIRED_MC_CREATE_COMP_MASK)
490     );
491 }
492
493 /**********************************************************************
494 Generate the response MAD
495 **********************************************************************/
496 static void
497 __osm_mcmr_rcv_respond(
498   IN const osm_mcmr_recv_t* const p_rcv,
499   IN const osm_madw_t* const p_madw,
500   IN ib_member_rec_t *p_mcmember_rec )
501 {
502   osm_madw_t   *p_resp_madw;
503   ib_sa_mad_t *p_sa_mad, *p_resp_sa_mad;
504   ib_member_rec_t *p_resp_mcmember_rec;
505   ib_api_status_t status;
506
507   OSM_LOG_ENTER( p_rcv->p_log, __osm_mcmr_rcv_respond );
508   /* 
509    *  Get a MAD to reply. Address of Mad is in the received mad_wrapper
510    */
511   p_resp_madw = osm_mad_pool_get(p_rcv->p_mad_pool,
512                                  p_madw->h_bind,
513                                  sizeof(ib_member_rec_t)+IB_SA_MAD_HDR_SIZE,
514                                  osm_madw_get_mad_addr_ptr(p_madw) );
515   if ( !p_resp_madw)
516   {
517     goto Exit;
518   }
519
520   p_resp_sa_mad = (ib_sa_mad_t*)p_resp_madw->p_mad;
521   p_sa_mad = (ib_sa_mad_t*)p_madw->p_mad;
522   /*  Copy the MAD header back into the response mad */
523   cl_memcpy(p_resp_sa_mad, p_sa_mad, IB_SA_MAD_HDR_SIZE);
524   /*  based on the current method decide about the response: */
525   if ((p_resp_sa_mad->method == IB_MAD_METHOD_GET) ||
526       (p_resp_sa_mad->method == IB_MAD_METHOD_SET)) {
527     p_resp_sa_mad->method = IB_MAD_METHOD_GET_RESP;
528   } else if (p_resp_sa_mad->method == IB_MAD_METHOD_DELETE) {
529     p_resp_sa_mad->method = (uint8_t)(p_resp_sa_mad->method | 0x80);
530   }
531   else
532   {
533     CL_ASSERT( p_resp_sa_mad->method == 0);
534   }
535
536   /* C15-0.1.5 - always return SM_Key = 0 (table 151 p 782) */
537   p_resp_sa_mad->sm_key = 0;
538
539   /* Fill in the offset (paylen will be done by the rmpp SAR) */
540   p_resp_sa_mad->attr_offset =
541     ib_get_attr_offset( sizeof(ib_member_rec_t) );
542   p_resp_mcmember_rec = (ib_member_rec_t*)&p_resp_sa_mad->data;
543
544   *p_resp_mcmember_rec = *p_mcmember_rec;
545
546   /* Fill in the mtu, rate, and packet lifetime selectors */
547   p_resp_mcmember_rec->mtu |= 2<<6; /* exactly */
548   p_resp_mcmember_rec->rate |=  2<<6; /* exactly */
549   p_resp_mcmember_rec->pkt_life |= 2<<6; /* exactly */
550
551   status = osm_vendor_send(
552     p_resp_madw->h_bind,
553     p_resp_madw,
554     FALSE);
555
556   if(status != IB_SUCCESS)
557   {
558     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
559              "__osm_mcmr_rcv_respond: ERR 1B07: \n"
560              "for TID = <0x%"PRIx64">\n", p_resp_sa_mad->trans_id);
561   }
562
563  Exit:
564   OSM_LOG_EXIT( p_rcv->p_log );
565   return;
566 }
567
568 /*********************************************************************
569 In joining an existing group, or when querying the mc groups,
570 we make sure the following components provided match: MTU and RATE
571 HACK: Currently we ignore the PKT_LIFETIME field.
572 **********************************************************************/
573 static boolean_t
574 __validate_more_comp_fields(
575   osm_log_t *p_log,
576   const osm_mgrp_t *p_mgrp,
577   const ib_member_rec_t *p_recvd_mcmember_rec,
578   ib_net64_t comp_mask)
579 {
580   uint8_t mtu_sel;
581   uint8_t mtu_required;
582   uint8_t mtu_mgrp;
583   uint8_t rate_sel;
584   uint8_t rate_required;
585   uint8_t rate_mgrp;
586
587   if ( comp_mask & IB_MCR_COMPMASK_MTU_SEL)
588   {
589     mtu_sel = (uint8_t)(p_recvd_mcmember_rec->mtu >> 6);
590     /* Clearing last 2 bits */
591     mtu_required = (uint8_t)(p_recvd_mcmember_rec->mtu & 0x3F);
592     mtu_mgrp = (uint8_t)(p_mgrp->mcmember_rec.mtu & 0x3F);
593     switch (mtu_sel)
594     {
595     case 0: /* Greater than MTU specified */
596       if(mtu_mgrp <= mtu_required)
597       {
598         osm_log( p_log, OSM_LOG_DEBUG,
599                  "__validate_more_comp_fields: "
600                  "Requested MTU %x is not greater than %x\n",
601                  mtu_mgrp, mtu_required);
602         return FALSE;
603       }
604       break;
605     case 1: /* Less than MTU specified */
606       if(mtu_mgrp >= mtu_required)
607       {
608         osm_log( p_log, OSM_LOG_DEBUG,
609                  "__validate_more_comp_fields: "
610                  "Requested MTU %x is not less than %x\n",
611                  mtu_mgrp, mtu_required);
612         return FALSE;
613       }
614       break;
615     case 2: /* Exactly MTU specified */
616       if(mtu_mgrp != mtu_required)
617       {
618         osm_log( p_log, OSM_LOG_DEBUG,
619                  "__validate_more_comp_fields: "
620                  "Requested MTU %x is not equal to %x\n",
621                  mtu_mgrp, mtu_required);
622         return FALSE;
623       }
624       break;
625     default:
626       break;
627     }
628   }
629
630   /* what about rate ? */
631   if ( comp_mask & IB_MCR_COMPMASK_RATE_SEL)
632   {
633     rate_sel = (uint8_t)(p_recvd_mcmember_rec->rate >> 6);
634     /* Clearing last 2 bits */
635     rate_required = (uint8_t)(p_recvd_mcmember_rec->rate & 0x3F);
636     rate_mgrp = (uint8_t)(p_mgrp->mcmember_rec.rate & 0x3F);
637     switch (rate_sel)
638     {
639     case 0: /* Greater than RATE specified */
640       if(rate_mgrp <= rate_required)
641       {
642         osm_log( p_log, OSM_LOG_DEBUG,
643                  "__validate_more_comp_fields: "
644                  "Requested RATE %x is not greater than %x\n",
645                  rate_mgrp, rate_required);
646         return FALSE;
647       }
648       break;
649     case 1: /* Less than RATE specified */
650       if(rate_mgrp >= rate_required)
651       {
652         osm_log( p_log, OSM_LOG_DEBUG,
653                  "__validate_more_comp_fields: "
654                  "Requested RATE %x is not less than %x\n",
655                  rate_mgrp, rate_required);
656         return FALSE;
657       }
658       break;
659     case 2: /* Exactly RATE specified */
660       if(rate_mgrp != rate_required)
661       {
662         osm_log( p_log, OSM_LOG_DEBUG,
663                  "__validate_more_comp_fields: "
664                  "Requested RATE %x is not equal to %x\n",
665                  rate_mgrp, rate_required);
666         return FALSE;
667       }
668       break;
669     default:
670       break;
671     }
672   }
673
674   return TRUE;
675 }
676
677 /*********************************************************************
678 In joining an existing group, we make sure the following components
679 are physically realizable: MTU and RATE
680 **********************************************************************/
681 static boolean_t
682 __validate_port_caps(
683   osm_log_t * const p_log,
684   const osm_mgrp_t *p_mgrp,
685   const osm_physp_t *p_physp)
686 {
687   ib_port_info_t *p_pi;
688   uint8_t mtu_required;
689   uint8_t mtu_mgrp;
690   uint8_t rate_required;
691   uint8_t rate_mgrp;
692
693   p_pi = osm_physp_get_port_info_ptr(p_physp);
694   if (!p_pi)
695   {
696     osm_log( p_log, OSM_LOG_DEBUG,
697             "__validate_port_caps: "
698             "Cannot get Port's 0x%016" PRIx64 " PortInfo\n",
699             osm_physp_get_port_guid(p_physp));
700     return FALSE;
701   }
702
703   mtu_required = ib_port_info_get_mtu_cap(p_pi);
704   mtu_mgrp = (uint8_t)(p_mgrp->mcmember_rec.mtu & 0x3F);
705   if (mtu_required < mtu_mgrp)
706   {
707     osm_log( p_log, OSM_LOG_DEBUG,
708             "__validate_port_caps: "
709             "Port's MTU %x is less than %x\n",
710              mtu_required, mtu_mgrp);
711     return FALSE;
712   }
713
714   rate_required = ib_port_info_compute_rate(p_pi);
715   rate_mgrp = (uint8_t)(p_mgrp->mcmember_rec.rate & 0x3F);
716   if (rate_required < rate_mgrp)
717   {
718     osm_log( p_log, OSM_LOG_DEBUG,
719             "__validate_port_caps: "
720             "Port's RATE %x is less than %x\n",
721              rate_required, rate_mgrp);
722     return FALSE;
723   }
724
725   return TRUE;
726 }
727
728 /**********************************************************************
729  * o15-0.2.1: If SA supports UD multicast, then if SA receives a SubnAdmSet()
730  * or SubnAdmDelete() method that would modify an existing
731  * MCMemberRecord, SA shall not modify that MCMemberRecord and shall
732  * return an error status of ERR_REQ_INVALID in response in the
733  * following cases:
734  * 1. Saved MCMemberRecord.ProxyJoin is not set and the request is
735  * issued by a requester with a GID other than the Port-GID.
736  * 2. Saved MCMemberRecord.ProxyJoin is set and the requester is not
737  * part of the partition for that MCMemberRecord.
738  **********************************************************************/
739 static boolean_t
740 __validate_modify(IN osm_mcmr_recv_t* const p_rcv,
741                   IN osm_mgrp_t* p_mgrp,
742                   IN osm_mad_addr_t* p_mad_addr,
743                   IN ib_member_rec_t* p_recvd_mcmember_rec,
744                   OUT osm_mcm_port_t **pp_mcm_port) {
745   ib_net64_t portguid;
746   ib_gid_t request_gid;
747   osm_physp_t* p_request_physp;
748
749   portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
750
751   *pp_mcm_port = NULL;
752
753   /* o15-0.2.1: If this is a new port being added - nothing to check */
754   if (! osm_mgrp_is_port_present(p_mgrp, portguid, pp_mcm_port))
755   {
756     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
757              "__validate_modify: "
758              "This is a new port in the MC group\n");
759     return TRUE;
760   }
761  
762   /* We validate the request according the the proxy_join.
763      Check if the proxy_join is set or not */
764   if ( (*pp_mcm_port)->proxy_join == FALSE )
765   {
766     /* The proxy_join is not set. Modifying can by done only
767        if the requester GID == PortGID */
768     request_gid = osm_get_gid_by_mad_addr(p_rcv->p_log,
769                                           p_rcv->p_subn,
770                                           p_mad_addr );
771
772     if (cl_memcmp(&((*pp_mcm_port)->port_gid), &request_gid, sizeof(ib_gid_t)))
773     {
774       osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
775                "__validate_modify: "
776                "No ProxyJoin but different ports: stored:0x%016"PRIx64
777                " request:0x%016"PRIx64"\n",
778                cl_ntoh64((*pp_mcm_port)->port_gid.unicast.interface_id),
779                cl_ntoh64(p_mad_addr->addr_type.gsi.grh_info.src_gid.unicast.interface_id)
780                );
781       return FALSE;
782     }
783   }
784   else
785   {
786     /* the proxy_join is set. Modification allowed only if the
787        requester is part of the partition for this MCMemberRecord */
788     p_request_physp = osm_get_physp_by_mad_addr(p_rcv->p_log,
789                                                 p_rcv->p_subn,
790                                                 p_mad_addr );
791     if (p_request_physp == NULL)
792       return FALSE;
793
794
795     if ( ! osm_physp_has_pkey(p_rcv->p_log, p_mgrp->mcmember_rec.pkey,
796                               p_request_physp ))
797     {
798       /* the request port is not part of the partition for this mgrp */
799       osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
800                "__validate_modify: "
801                "ProxyJoin but port not in partition. stored:0x%016"PRIx64
802                " request:0x%016"PRIx64"\n",
803                cl_ntoh64((*pp_mcm_port)->port_gid.unicast.interface_id),
804                cl_ntoh64(p_mad_addr->addr_type.gsi.grh_info.src_gid.unicast.interface_id)
805                );
806       return FALSE;
807     }
808   }
809   return TRUE;
810 }
811
812 /**********************************************************************
813  **********************************************************************/
814 /*
815  * Check legality of the requested MGID DELETE
816  * o15-0.1.14 = VALID DELETE:
817  * To be a valid delete MAD needs to:
818  * 1 the MADs PortGID and MGID components match the PortGID and
819  *   MGID of a stored MCMemberRecord;
820  * 2 the MADs JoinState component contains at least one bit set to 1
821  *   in the same position as that stored MCMemberRecords JoinState
822  *   has a bit set to 1,
823  *   i.e., the logical AND of the two JoinState components
824  *   is not all zeros;
825  * 3 the MADs JoinState component does not have some bits set
826  *   which are not set in the stored MCMemberRecords JoinState component;
827  * 4 either the stored MCMemberRecord:ProxyJoin is reset (0), and the
828  *   MADs source is the stored PortGID;
829  *   OR
830  *   the stored MCMemberRecord:ProxyJoin is set (1), (see o15-
831  *   0.1.2:); and the MADs source is a member of the partition indicated
832  *   by the stored MCMemberRecord:P_Key.
833  */
834 static boolean_t
835 __validate_delete(IN osm_mcmr_recv_t* const p_rcv,
836                   IN osm_mgrp_t *p_mgrp,
837                   IN osm_mad_addr_t* p_mad_addr,
838                   IN ib_member_rec_t* p_recvd_mcmember_rec,
839                   OUT osm_mcm_port_t **pp_mcm_port) {
840   ib_net64_t portguid;
841   portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
842
843   *pp_mcm_port = NULL;
844
845   /* 1 */
846   if (! osm_mgrp_is_port_present(p_mgrp, portguid, pp_mcm_port))
847   {
848     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
849              "__validate_delete: "
850              "Failed to find the port in the MC group\n");
851     return FALSE;
852   }
853
854   /* 2 */
855   if (! (p_recvd_mcmember_rec->scope_state & 0x0F &
856          (*pp_mcm_port)->scope_state))
857   {
858     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
859              "__validate_delete: "
860              "Could not find any matching bits in the stored and requested JoinState\n");
861     return FALSE;
862   }
863
864   /* 3 */
865   if ( ((p_recvd_mcmember_rec->scope_state & 0x0F) |
866         (0x0F & (*pp_mcm_port)->scope_state)) !=
867        (0x0F & (*pp_mcm_port)->scope_state))
868   {
869     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
870              "__validate_delete: "
871              "Some bits in the request JoinState (0x%X) are not set in the stored port (0x%X)\n",
872              (p_recvd_mcmember_rec->scope_state & 0x0F),
873              (0x0F & (*pp_mcm_port)->scope_state)
874              );
875     return FALSE;
876   }
877
878   /* 4 */
879   /* Validate according the the proxy_join (o15-0.1.2) */
880   if ( __validate_modify( p_rcv, p_mgrp, p_mad_addr, p_recvd_mcmember_rec,
881                           pp_mcm_port ) == FALSE )
882   {
883     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
884              "__validate_delete: "
885              "proxy_join validation failure\n" );
886     return FALSE;
887   }
888   return TRUE;
889 }
890
891 /**********************************************************************
892  **********************************************************************/
893 /*
894  * Check legality of the requested MGID (note this does not hold for SA
895  * created MGIDs
896  *
897  * Implementing o16.0.1.6:
898  * A multicast GID is considered to be invalid if:
899  * 1. It does not comply with the rules as specified in 4.1.1 "GID Usage and
900  *    Properties" on page 133:
901  *
902  * 14) The multicast GID format is (bytes are comma sep):
903  *     0xff,<Fl><Sc>,<Si>,<Si>,<P>,<P>,<P>,<P>,<P>,<P>,<P>,<P>,<Id>,<Id>,<Id>,<Id>
904  *     Fl  4bit = Flags (b)
905  *     Sc  4bit = Scope (c)
906  *     Si 16bit = Signature (2)
907  *     P  64bit = GID Prefix (should be a subnet unique ID - normally Subnet Prefix)
908  *     Id 32bit = Unique ID in the Subnet (might be MLID or Pkey ?)
909  *
910  *  a) 8-bits of 11111111 at the start of the GID identifies this as being a
911  *     multicast GID.
912  *  b) Flags is a set of four 1-bit flags: 000T with three flags reserved
913  *     and defined as zero (0). The T flag is defined as follows:
914  *     i) T = 0 indicates this is a permanently assigned (i.e. wellknown)
915  *        multicast GID. See RFC 2373 and RFC 2375 as reference
916  *        for these permanently assigned GIDs.
917  *     ii) T = 1 indicates this is a non-permanently assigned (i.e. transient)
918  *        multicast GID.
919  *  c) Scope is a 4-bit multicast scope value used to limit the scope of
920  *     the multicast group. The following table defines scope value and
921  *     interpretation.
922  *
923  *     Multicast Address Scope Values:
924  *     0x2 Link-local
925  *     0x5 Site-local
926  *     0x8 Organization-local
927  *     0xE Global
928  *
929  * 2. It contains the SA-specific signature of 0xA01B and has the link-local
930  *    scope bits set. (EZ: the idea here is that SA created MGIDs are the
931  *    only source for this signature with link-local scope)
932  */
933 ib_api_status_t
934 __validate_requested_mgid(IN osm_mcmr_recv_t* const p_rcv,
935                           IN const ib_member_rec_t* p_mcm_rec) {
936
937   uint16_t signature;
938   boolean_t valid = TRUE;
939
940   OSM_LOG_ENTER( p_rcv->p_log, __validate_requested_mgid );
941
942   /* 14-a: mcast GID must start with 0xFF */
943   if (p_mcm_rec->mgid.multicast.header[0] != 0xFF)
944   {
945     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
946              "__validate_requested_mgid: ERR 1B01: "
947              "Wrong MGID Prefix 0x%02X must be 0xFF\n",
948              cl_ntoh16(p_mcm_rec->mgid.multicast.header[0])
949              );
950     valid = FALSE;
951     goto Exit;
952   }
953
954   /* the MGID signature can mark IPoIB or SA assigned MGIDs */
955   cl_memcpy(&signature, &(p_mcm_rec->mgid.multicast.raw_group_id), sizeof(signature));
956   signature = cl_ntoh16(signature);
957   osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
958            "__validate_requested_mgid:  "
959            "MGID Signed as 0x%04X\n",
960            signature
961            );
962
963   /*
964    * We skip any checks for MGIDs that follow IPoIB
965    * GID structure as defined by the IETF ipoib-link-multicast.
966    *
967    * For IPv4 over IB, the signature will be "0x401B".
968    *
969    * |   8    |  4 |  4 |     16 bits     | 16 bits | 48 bits  | 32 bits |
970    * +--------+----+----+-----------------+---------+----------+---------+
971    * |11111111|0001|scop|<IPoIB signature>|< P_Key >|00.......0|<all 1's>|
972    * +--------+----+----+-----------------+---------+----------+---------+
973    *
974    * For IPv6 over IB, the signature will be "0x601B".
975    *
976    * |   8    |  4 |  4 |     16 bits     | 16 bits |       80 bits      |
977    * +--------+----+----+-----------------+---------+--------------------+
978    * |11111111|0001|scop|<IPoIB signature>|< P_Key >|000.............0001|
979    * +--------+----+----+-----------------+---------+--------------------+
980    *
981    */
982   if (signature == 0x401B || signature == 0x601B)
983   {
984     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
985              "__validate_requested_mgid:  "
986              "Skipping MGID Validation for IPoIB Signed (0x%04X) MGIDs\n",
987              signature
988              );
989     goto Exit;
990   }
991
992   /* 14-b: the 3 upper bits in the "flags" should be zero: */
993   if ( p_mcm_rec->mgid.multicast.header[1] & 0xE0 )
994   {
995     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
996              "__validate_requested_mgid: ERR 1B28: "
997              "MGID uses Reserved Flags: flags=0x%X\n",
998              (p_mcm_rec->mgid.multicast.header[1] & 0xE0) >> 4
999              );
1000     valid = FALSE;
1001     goto Exit;
1002   }
1003
1004   /* 2 - now what if the link local format 0xA01B is used -
1005      the scope should not be link local */
1006   if ( ( signature == 0xA01B ) &&
1007        ((p_mcm_rec->mgid.multicast.header[1] & 0x0F) == 0x02) ) {
1008     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1009              "__validate_requested_mgid: ERR 1B24: "
1010              "MGID Uses 0xA01B signature but with link-local scope\n");
1011     valid = FALSE;
1012     goto Exit;
1013   }
1014
1015   /*
1016    * There is no real way to make sure the Unique MGID Prefix is really unique.
1017    * If we could enforce using the Subnet Prefix for that purpose it would
1018    * have been nice. But the spec does not require it.
1019    */
1020
1021  Exit:
1022   OSM_LOG_EXIT( p_rcv->p_log );
1023   return (valid);
1024 }
1025
1026 /**********************************************************************
1027  Check if the requested new MC group parameters are realizable.
1028  Also set the default MTU and Rate if not provided by the user.
1029 **********************************************************************/
1030 boolean_t
1031 __mgrp_request_is_realizable(
1032   IN osm_mcmr_recv_t* const p_rcv,
1033   IN ib_net64_t comp_mask,
1034   IN ib_member_rec_t * p_mcm_rec,
1035   IN const osm_physp_t* const p_physp)
1036 {
1037   uint8_t mtu_sel;
1038   uint8_t mtu_required;
1039   uint8_t rate_sel;
1040   uint8_t rate_required;
1041   osm_log_t *p_log = p_rcv->p_log;
1042   ib_port_info_t *p_pi = NULL;
1043
1044   OSM_LOG_ENTER( p_rcv->p_log,  __mgrp_request_is_realizable;)
1045
1046    if (p_physp != NULL)
1047       p_pi = osm_physp_get_port_info_ptr(p_physp);
1048
1049   /*
1050    * End of o15-0.1.6 specifies:
1051    * ....
1052    * The entity may also supply the other components such as HopLimit, MTU,
1053    * etc. during group creation time. If these components are not provided
1054    * during group creation time, SA will provide them for the group. The values
1055    * chosen are vendor-dependent and beyond the scope of the specification.
1056    *
1057    * so we might also need to assign RATE/MTU if they are not comp masked in.
1058    */
1059   if (! (comp_mask & IB_MCR_COMPMASK_MTU))
1060   {
1061     p_mcm_rec->mtu = p_rcv->p_subn->min_ca_mtu;
1062   }
1063   else
1064   {
1065     /* we need to select an MTU based on the requested MTU and selector */
1066     if ( comp_mask & IB_MCR_COMPMASK_MTU_SEL)
1067     {
1068       mtu_sel = (uint8_t)(p_mcm_rec->mtu >> 6);
1069     } 
1070     else 
1071     {
1072       /* by default we assume an exact mtu is requested */
1073       mtu_sel = 2; 
1074     }
1075     
1076     /* Clearing last 2 bits */
1077     mtu_required = (uint8_t)(p_mcm_rec->mtu & 0x3F);
1078
1079     switch (mtu_sel)
1080     {
1081     case 0: /* Greater than MTU specified */
1082       /* we provide the largest MTU possible if we can */
1083       if (mtu_required < p_rcv->p_subn->min_ca_mtu)
1084       {
1085         p_mcm_rec->mtu = p_rcv->p_subn->min_ca_mtu;
1086       } 
1087       else
1088       {
1089         osm_log( p_log, OSM_LOG_DEBUG,
1090                  "__mgrp_request_is_realizable: "
1091                  "Requested MTU %x >= the maximal possible:%x\n",
1092                  mtu_required, p_rcv->p_subn->min_ca_mtu);
1093         return FALSE;
1094       }
1095       break;
1096     case 1: /* Less than MTU specified */
1097       /* if the requested MTU is not already the minimal, then we will
1098          use the smaller of the two:
1099          a. one lower then the required
1100          b. the mtu of the requesting port
1101          If the p_pi is NULL, this means there is no requester port and
1102          just use mtu one lower than the required. */
1103       if ( mtu_required > 1 )
1104       {
1105         if (p_pi && ib_port_info_get_mtu_cap(p_pi) < (mtu_required - 1))
1106           p_mcm_rec->mtu = (mtu_sel<<6) | ib_port_info_get_mtu_cap(p_pi);
1107         else
1108           p_mcm_rec->mtu--;
1109       }
1110       else
1111       {
1112         osm_log( p_log, OSM_LOG_DEBUG,
1113                  "__mgrp_request_is_realizable: "
1114                  "Can not obtain a lower MTU then the given one:%x\n",
1115                  mtu_required);
1116         return FALSE;
1117       }
1118       break;
1119     case 2: /* Exactly MTU specified */
1120       /* make sure it is in the range */
1121       if (mtu_required < IB_MIN_MTU || mtu_required > IB_MAX_MTU)
1122       {
1123         osm_log( p_log, OSM_LOG_DEBUG,
1124                  "__mgrp_request_is_realizable: "
1125                  "Requested MTU %x is out of range\n",
1126                  mtu_required);
1127         return FALSE;
1128       }
1129       break;
1130     default:
1131       break;
1132     }
1133   }
1134
1135   if (! (comp_mask & IB_MCR_COMPMASK_RATE))
1136   {
1137     p_mcm_rec->rate = p_rcv->p_subn->min_ca_rate;
1138   }
1139   else
1140   {
1141     /* we need to select an RATE based on the requested RATE and selector */
1142     if ( comp_mask & IB_MCR_COMPMASK_RATE_SEL)
1143     {
1144       rate_sel = (uint8_t)(p_mcm_rec->rate >> 6);
1145     } 
1146     else 
1147     {
1148       /* by default we assume an exact rate is requested */
1149       rate_sel = 2; 
1150     }
1151     
1152     /* Clearing last 2 bits */
1153     rate_required = (uint8_t)(p_mcm_rec->rate & 0x3F);
1154
1155     switch (rate_sel)
1156     {
1157     case 0: /* Greater than RATE specified */
1158       /* we provide the largest RATE possible if we can */
1159       if (rate_required < p_rcv->p_subn->min_ca_rate)
1160       {
1161         p_mcm_rec->rate = p_rcv->p_subn->min_ca_rate;
1162       } 
1163       else
1164       {
1165         osm_log( p_log, OSM_LOG_DEBUG,
1166                  "__mgrp_request_is_realizable: "
1167                  "Requested RATE %x >= the maximal possible:%x\n",
1168                  rate_required, p_rcv->p_subn->min_ca_rate);
1169         return FALSE;
1170       }
1171       break;
1172     case 1: /* Less than RATE specified */
1173       /* if the requested RATE is not already the minimal, then we will
1174          use the smaller of the two:
1175          a. one lower then the required
1176          b. the rate of the requesting port
1177          If the p_pi is NULL, this means there is no requester port and
1178          just use rate one lower than the required. */
1179       
1180       if ( rate_required > 2 )
1181       {
1182         if (p_pi && ib_port_info_compute_rate(p_pi) < (rate_required - 1))
1183           p_mcm_rec->rate = (rate_sel<<6) | ib_port_info_compute_rate(p_pi);
1184         else
1185           p_mcm_rec->rate--;
1186       }
1187       else
1188       {
1189         osm_log( p_log, OSM_LOG_DEBUG,
1190                  "__mgrp_request_is_realizable: "
1191                  "Can not obtain a lower RATE then the given one:%x\n",
1192                  rate_required);
1193         return FALSE;
1194       }
1195       break;
1196     case 2: /* Exactly RATE specified */
1197       /* make sure it is in the range */
1198       if (rate_required < IB_MIN_RATE || rate_required > IB_MAX_RATE)
1199       {
1200         osm_log( p_log, OSM_LOG_DEBUG,
1201                  "__mgrp_request_is_realizable: "
1202                  "Requested RATE %x is out of range\n",
1203                  rate_required);
1204         return FALSE;
1205       }
1206       break;
1207     default:
1208       break;
1209     }
1210   }
1211
1212   OSM_LOG_EXIT( p_rcv->p_log );
1213   return TRUE;
1214 }
1215   
1216 /**********************************************************************
1217  Call this function to create a new mgrp.
1218 **********************************************************************/
1219 ib_api_status_t
1220 osm_mcmr_rcv_create_new_mgrp(
1221   IN osm_mcmr_recv_t* const p_rcv,
1222   IN ib_net64_t comp_mask,
1223   IN const ib_member_rec_t* const p_recvd_mcmember_rec,
1224   IN const osm_physp_t* const p_physp,
1225   OUT osm_mgrp_t **pp_mgrp)
1226 {
1227   ib_net16_t mlid;
1228   uint8_t zero_mgid, valid;
1229   uint8_t scope, i;
1230   ib_gid_t *p_mgid;
1231   osm_mgrp_t *p_prev_mgrp;
1232   ib_api_status_t status = IB_SUCCESS;
1233
1234   /*  copy for modifications */
1235   ib_member_rec_t mcm_rec = *p_recvd_mcmember_rec;
1236
1237   OSM_LOG_ENTER( p_rcv->p_log, osm_mcmr_rcv_create_new_mgrp );
1238
1239   /* but what if the given MGID was not 0 ? */
1240   zero_mgid = 1;
1241   for ( i = 0 ; i < sizeof(p_recvd_mcmember_rec->mgid); i++ )
1242   {
1243     if (p_recvd_mcmember_rec->mgid.raw[i] != 0)
1244     {
1245       zero_mgid = 0;
1246       break;
1247     }
1248   }
1249
1250   /*
1251     we allocate a new mlid number before we might use it
1252     for MGID ...
1253   */
1254   mlid = __get_new_mlid(p_rcv);
1255   if ( mlid == 0 )
1256   {
1257     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1258              "osm_mcmr_rcv_create_new_mgrp: ERR 1B19: "
1259              "__get_new_mlid failed\n");
1260     status = IB_SA_MAD_STATUS_NO_RESOURCES;
1261     goto Exit;
1262   }
1263
1264   osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1265            "osm_mcmr_rcv_create_new_mgrp: "
1266            "Getting new mlid 0x%x\n", cl_ntoh16(mlid));
1267
1268   /* we need to create the new MGID if it was not defined */
1269   if (zero_mgid)
1270   {
1271     /* create a new MGID */
1272
1273     /* use the given scope state only if requested! */
1274     if (comp_mask & IB_MCR_COMPMASK_SCOPE)
1275     {
1276       ib_member_get_scope_state(
1277         p_recvd_mcmember_rec->scope_state, &scope, NULL);
1278     }
1279     else
1280     {
1281       /* to guarantee no collision with other subnets use local scope! */
1282       scope = 0x02; /*  link-local scope */
1283     }
1284
1285     p_mgid = &(mcm_rec.mgid);
1286     p_mgid->raw[0] = 0xFF;
1287     p_mgid->raw[1] = 0x10 | scope;
1288     p_mgid->raw[2] = 0xA0;
1289     p_mgid->raw[3] = 0x1B;
1290
1291     /*  HACK: I will be using the SA port gid for making it globally unique */
1292     cl_memcpy((&p_mgid->raw[4]),
1293               &p_rcv->p_subn->opt.subnet_prefix, sizeof(uint64_t));
1294
1295     /*  HACK how do we get a unique number - use the mlid twice */
1296     cl_memcpy(&p_mgid->raw[10], &mlid, sizeof(uint16_t));
1297     cl_memcpy(&p_mgid->raw[12], &mlid, sizeof(uint16_t));
1298     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1299              "osm_mcmr_rcv_create_new_mgrp: "
1300              "Allocated new MGID:0x%016" PRIx64 " : "
1301              "0x%016" PRIx64 "\n",
1302              cl_ntoh64(p_mgid->unicast.prefix),
1303              cl_ntoh64(p_mgid->unicast.interface_id) );
1304   }
1305   else
1306   {
1307
1308     /* a specific MGID was requested so validate the resulting MGID */
1309     valid = __validate_requested_mgid(p_rcv, &mcm_rec);
1310     if (! valid)
1311     {
1312       osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1313                "osm_mcmr_rcv_create_new_mgrp: ERR 1B22: "
1314                "Invalid requested MGID\n");
1315       __free_mlid(p_rcv, mlid);
1316       status = IB_SA_MAD_STATUS_REQ_INVALID;
1317       goto Exit;
1318     }
1319   }
1320
1321   /* check the requested parameters are realizable */
1322   if (__mgrp_request_is_realizable(p_rcv, comp_mask, &mcm_rec, p_physp) == FALSE)
1323   {
1324     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1325              "osm_mcmr_rcv_create_new_mgrp: ERR 1B26: "
1326              "Requested MGRP parameters are not realizable\n");
1327     __free_mlid(p_rcv, mlid);
1328     status = IB_SA_MAD_STATUS_REQ_INVALID;
1329     goto Exit;
1330   }
1331
1332   /* create a new MC Group */
1333   *pp_mgrp = osm_mgrp_new(mlid);
1334   if (*pp_mgrp == NULL)
1335   {
1336     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1337              "osm_mcmr_rcv_create_new_mgrp: ERR 1B08: "
1338              "osm_mgrp_new failed\n");
1339     __free_mlid(p_rcv, mlid);
1340     status =  IB_SA_MAD_STATUS_NO_RESOURCES;
1341     goto Exit;
1342   }
1343
1344   /* the mcmember_record should have mtu_sel, rate_sel and pkt_lifetime_sel = 2 */
1345   (*pp_mgrp)->mcmember_rec.mtu |= 2<<6; /* exactly */
1346   (*pp_mgrp)->mcmember_rec.rate |= 2<<6; /* exactly */
1347   (*pp_mgrp)->mcmember_rec.pkt_life |= 2<<6; /* exactly */
1348
1349   /* Initialize the mgrp */
1350   (*pp_mgrp)->mcmember_rec = mcm_rec;
1351   (*pp_mgrp)->mcmember_rec.mlid = mlid;
1352
1353   /* Insert the new group in the data base */
1354   
1355   /* since we might have an old group by that mlid
1356      one that its deletion was delayed for an idle time
1357      we need to deallocate it first */
1358   p_prev_mgrp = (osm_mgrp_t *)cl_qmap_get(&p_rcv->p_subn->mgrp_mlid_tbl, mlid);
1359   if (p_prev_mgrp != (osm_mgrp_t *)cl_qmap_end(&p_rcv->p_subn->mgrp_mlid_tbl))
1360   {
1361     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1362              "osm_mcmr_rcv_create_new_mgrp: "
1363              "Found previous group for mlid:0x%04x - Need to destroy it\n",
1364              cl_ntoh16(mlid));
1365     cl_qmap_remove_item(&p_rcv->p_subn->mgrp_mlid_tbl,
1366                         (cl_map_item_t *)p_prev_mgrp );
1367     osm_mgrp_destroy( p_prev_mgrp );
1368   }
1369
1370   cl_qmap_insert(&p_rcv->p_subn->mgrp_mlid_tbl,
1371                  mlid,
1372                  &(*pp_mgrp)->map_item);
1373
1374   /* Send a Report to any InformInfo registerd for
1375      Trap 66: MCGroup create */
1376   osm_mgrp_send_create_notice(p_rcv->p_subn, p_rcv->p_log, *pp_mgrp);
1377
1378  Exit:
1379   OSM_LOG_EXIT( p_rcv->p_log );
1380   return status;
1381
1382 }
1383
1384 /*********************************************************************
1385 Process a request for leaving the group
1386 **********************************************************************/
1387 static void
1388 osm_mcmr_rcv_leave_mgrp(
1389   IN osm_mcmr_recv_t* const p_rcv,
1390   IN const osm_madw_t* const p_madw )
1391 {
1392   boolean_t valid;
1393   osm_mgrp_t *p_mgrp;
1394   ib_api_status_t status;
1395   ib_sa_mad_t *p_sa_mad;
1396   ib_member_rec_t*p_recvd_mcmember_rec;
1397   ib_member_rec_t mcmember_rec;
1398   ib_net16_t mlid;
1399   ib_net16_t sa_status;
1400   ib_net64_t portguid;
1401   osm_mcm_port_t *p_mcm_port;
1402   uint8_t port_join_state;
1403   uint8_t new_join_state;
1404  
1405   OSM_LOG_ENTER( p_rcv->p_log, osm_mcmr_rcv_leave_mgrp );
1406
1407   p_mgrp = NULL;
1408   p_sa_mad = osm_madw_get_sa_mad_ptr( p_madw );
1409   p_recvd_mcmember_rec =
1410     (ib_member_rec_t*)ib_sa_mad_get_payload_ptr( p_sa_mad );
1411
1412   mcmember_rec = *p_recvd_mcmember_rec;
1413
1414   if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
1415     osm_dump_mc_record( p_rcv->p_log, &mcmember_rec, OSM_LOG_DEBUG );
1416
1417   CL_PLOCK_EXCL_ACQUIRE(p_rcv->p_lock);
1418   status = __get_mgrp_by_mgid(p_rcv,p_recvd_mcmember_rec, &p_mgrp);
1419   if(status == IB_SUCCESS)
1420   {
1421     mlid = p_mgrp->mlid;
1422     portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
1423
1424     /* check validity of the delete request o15-0.1.14 */
1425     valid =  __validate_delete(p_rcv,
1426                                p_mgrp,
1427                                osm_madw_get_mad_addr_ptr(p_madw),
1428                                p_recvd_mcmember_rec,
1429                                &p_mcm_port);
1430
1431     if (valid)
1432     {
1433
1434       /*
1435        * according to the same o15-0.1.14 we get the stored JoinState and the
1436        * request JoinState and they must be opposite to leave -
1437        * otherwise just update it
1438        */
1439       port_join_state = p_mcm_port->scope_state & 0x0F;
1440       new_join_state =
1441         port_join_state & ~(p_recvd_mcmember_rec->scope_state & 0x0F);
1442       if (new_join_state)
1443       {
1444         osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1445                  "osm_mcmr_rcv_leave_mgrp: "
1446                  "After update JoinState != 0. Updating from 0x%X to 0x%X\n",
1447                  port_join_state,
1448                  new_join_state
1449                  );
1450         /* Just update the result JoinState */
1451         p_mcm_port->scope_state =
1452           new_join_state | (p_mcm_port->scope_state & 0xf0);
1453
1454         mcmember_rec.scope_state = p_mcm_port->scope_state;
1455       }
1456       else
1457       {
1458         /* we need to return the stored scope state */
1459         mcmember_rec.scope_state = p_mcm_port->scope_state;
1460
1461         /* OK we can leave */
1462         CL_PLOCK_RELEASE( p_rcv->p_lock );
1463
1464         status = osm_sm_mcgrp_leave(p_rcv->p_sm,
1465                                     mlid,
1466                                     portguid);
1467         if(status != IB_SUCCESS)
1468         {
1469           osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1470                    "osm_mcmr_rcv_leave_mgrp: ERR 1B09: "
1471                    "osm_sm_mcgrp_leave failed\n");
1472         }
1473
1474         CL_PLOCK_EXCL_ACQUIRE(p_rcv->p_lock);
1475         /* Note: The deletion of the mgrp itself will be done in the callback
1476            for the multicast tree updating (osm_mcast_mgr_process_mgrp_cb) */
1477       }
1478     }
1479     else
1480     {
1481       CL_PLOCK_RELEASE( p_rcv->p_lock );
1482       osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1483                "osm_mcmr_rcv_leave_mgrp: ERR 1B25: "
1484                "Received an invalid delete request on "
1485                "MGID: 0x%016" PRIx64 " : "
1486                "0x%016" PRIx64 " for "
1487                "PortGID: 0x%016" PRIx64 " : "
1488                "0x%016" PRIx64 "\n",
1489                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.prefix ),
1490                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.interface_id ),
1491                cl_ntoh64( p_recvd_mcmember_rec->port_gid.unicast.prefix ),
1492                cl_ntoh64( p_recvd_mcmember_rec->port_gid.unicast.interface_id ) );
1493       sa_status   = IB_SA_MAD_STATUS_REQ_INVALID;
1494       osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1495       goto Exit;
1496     }
1497
1498   }
1499   else
1500   {
1501     CL_PLOCK_RELEASE( p_rcv->p_lock );
1502     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1503              "osm_mcmr_rcv_leave_mgrp: "
1504              "Multicast group not present failed\n");
1505     sa_status = IB_SA_MAD_STATUS_REQ_INVALID;
1506     osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1507     goto Exit;
1508   }
1509
1510   CL_PLOCK_RELEASE( p_rcv->p_lock );
1511
1512   /* Send an SA RESPONSE */
1513   __osm_mcmr_rcv_respond( p_rcv,
1514                           p_madw,
1515                           &mcmember_rec );
1516
1517  Exit:
1518   OSM_LOG_EXIT( p_rcv->p_log );
1519   return;
1520 }
1521
1522 /**********************************************************************
1523  Handle a join (or create) request
1524 **********************************************************************/
1525 static
1526 void
1527 osm_mcmr_rcv_join_mgrp(
1528   IN osm_mcmr_recv_t* const p_rcv,
1529   IN const osm_madw_t* const p_madw
1530   )
1531 {
1532   boolean_t valid;
1533   osm_mgrp_t *p_mgrp = NULL;
1534   ib_api_status_t status;
1535   ib_sa_mad_t *p_sa_mad;
1536   ib_member_rec_t*p_recvd_mcmember_rec;
1537   ib_member_rec_t mcmember_rec;
1538   ib_net16_t sa_status;
1539   ib_net16_t mlid;
1540   osm_mcm_port_t *p_mcmr_port;
1541   ib_net64_t portguid;
1542   osm_port_t * p_port;
1543   osm_physp_t* p_physp;
1544   osm_physp_t* p_request_physp;
1545   uint8_t is_new_group; /*  TRUE = there is a need to create a group */
1546   osm_mcast_req_type_t req_type;
1547   uint8_t join_state;
1548
1549   OSM_LOG_ENTER( p_rcv->p_log, osm_mcmr_rcv_join_mgrp );
1550
1551   p_mgrp = NULL;
1552   p_sa_mad = osm_madw_get_sa_mad_ptr( p_madw );
1553   p_recvd_mcmember_rec =
1554     (ib_member_rec_t*)ib_sa_mad_get_payload_ptr( p_sa_mad );
1555
1556   portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
1557
1558   mcmember_rec = *p_recvd_mcmember_rec;
1559
1560   if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
1561   {
1562     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1563              "osm_mcmr_rcv_join_mgrp: "
1564              "Dump of incoming record\n");
1565     osm_dump_mc_record( p_rcv->p_log, &mcmember_rec, OSM_LOG_DEBUG );
1566   }
1567
1568   CL_PLOCK_EXCL_ACQUIRE(p_rcv->p_lock);
1569
1570   /* make sure the requested port guid is known to the SM */
1571   p_port = (osm_port_t *)cl_qmap_get(&p_rcv->p_subn->port_guid_tbl,
1572                                      portguid);
1573
1574   if (p_port == (osm_port_t *)cl_qmap_end(&p_rcv->p_subn->port_guid_tbl))
1575   {
1576     CL_PLOCK_RELEASE( p_rcv->p_lock );
1577
1578     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1579              "osm_mcmr_rcv_join_mgrp: "
1580              "Unknown portguid 0x%016" PRIx64 "\n",
1581              portguid);
1582     sa_status  = IB_SA_MAD_STATUS_REQ_INVALID;
1583     osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1584     goto Exit;
1585   }
1586
1587   p_physp = osm_port_get_default_phys_ptr(p_port);
1588   /* Check that the p_physp and the requester physp are in the same
1589      partition. */
1590   p_request_physp =
1591     osm_get_physp_by_mad_addr(p_rcv->p_log,
1592                               p_rcv->p_subn,
1593                               osm_madw_get_mad_addr_ptr(p_madw) );
1594   if (p_request_physp == NULL)
1595   {
1596     CL_PLOCK_RELEASE( p_rcv->p_lock );
1597     goto Exit;
1598   }
1599
1600   if (! osm_physp_share_pkey( p_rcv->p_log, p_physp, p_request_physp))
1601   {
1602     CL_PLOCK_RELEASE( p_rcv->p_lock );
1603
1604     osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
1605              "osm_mcmr_rcv_join_mgrp: "
1606              "port and requester don't share pkey\n" );
1607     sa_status  = IB_SA_MAD_STATUS_REQ_INVALID;
1608     osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1609     goto Exit;
1610   }
1611  
1612   ib_member_get_scope_state(
1613     p_recvd_mcmember_rec->scope_state, NULL, &join_state);
1614
1615   /* do we need to create a new group? */
1616   status = __get_mgrp_by_mgid(p_rcv, p_recvd_mcmember_rec, &p_mgrp);
1617   if ((status == IB_NOT_FOUND) || p_mgrp->to_be_deleted)
1618   {
1619     /* check for JoinState.FullMember = 1 o15.0.1.9 */
1620     if ((join_state & 0x01) != 0x01)
1621     {
1622       CL_PLOCK_RELEASE( p_rcv->p_lock );
1623       osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1624                "osm_mcmr_rcv_join_mgrp: ERR 1B10: "
1625                "Provided Join State != FullMember - required for create, "
1626                "MGID: 0x%016" PRIx64 " : "
1627                "0x%016" PRIx64 "\n",
1628                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.prefix ),
1629                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.interface_id ) );
1630       sa_status = IB_SA_MAD_STATUS_REQ_INVALID;
1631       osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1632       goto Exit;
1633     }
1634
1635     /* check for the comp_mask */
1636     valid = __check_create_comp_mask(p_sa_mad->comp_mask,
1637                                      p_recvd_mcmember_rec);
1638     if (valid)
1639     {
1640       status = osm_mcmr_rcv_create_new_mgrp(p_rcv,
1641                                             p_sa_mad->comp_mask,
1642                                             p_recvd_mcmember_rec,
1643                                             p_physp,
1644                                             &p_mgrp);
1645       if (status != IB_SUCCESS)
1646       {
1647         CL_PLOCK_RELEASE( p_rcv->p_lock );
1648         sa_status = status;
1649         osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1650         goto Exit;
1651       }
1652       /* copy the MGID to the result */
1653       mcmember_rec.mgid = p_mgrp->mcmember_rec.mgid;
1654     }
1655     else
1656     {
1657       osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1658                "osm_mcmr_rcv_join_mgrp: ERR 1B11: "
1659                "method = %s, "
1660                "scope_state = 0x%x, "
1661                "component mask = 0x%016" PRIx64 ", "
1662                "expected comp mask = 0x%016" PRIx64 ", "
1663                "MGID: 0x%016" PRIx64 " : "
1664                "0x%016" PRIx64 "\n",
1665                ib_get_sa_method_str(p_sa_mad->method),
1666                p_recvd_mcmember_rec->scope_state,
1667                cl_ntoh64(p_sa_mad->comp_mask),
1668                CL_NTOH64(REQUIRED_MC_CREATE_COMP_MASK),
1669                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.prefix ),
1670                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.interface_id ) );
1671
1672       CL_PLOCK_RELEASE( p_rcv->p_lock );
1673       sa_status   = IB_SA_MAD_STATUS_INSUF_COMPS;
1674       osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1675       goto Exit;
1676     }
1677     is_new_group = 1;
1678     req_type = OSM_MCAST_REQ_TYPE_CREATE;
1679   }
1680   else
1681   {
1682     /* no need for a new group */
1683     is_new_group = 0;
1684     req_type = OSM_MCAST_REQ_TYPE_JOIN;
1685   }
1686
1687   CL_ASSERT(p_mgrp);
1688   mlid = p_mgrp->mlid;
1689
1690   /*
1691    * o15-0.2.4: If SA supports UD multicast, then SA shall cause an
1692    * endport to join an existing multicast group if:
1693    * 1. It receives a SubnAdmSet() method for a MCMemberRecord, and
1694    *    - WE KNOW THAT ALREADY
1695    * 2. The MGID is specified and matches an existing multicast
1696    *    group, and
1697    *    - WE KNOW THAT ALREADY
1698    * 3. The MCMemberRecord:JoinState is not all 0s, and
1699    * 4. PortGID is specified and
1700    *    - WE KNOW THAT ALREADY (as it matched a real one)
1701    * 5. All other components match that existing group, either by
1702    *    being wildcarded or by having values identical to those specified
1703    *    by the component mask and in use by the group with the exception
1704    *    of components such as ProxyJoin and Reserved, which are ignored
1705    *    by SA.
1706    *
1707    * We we need to check #3 and #5 here:
1708    */
1709   valid = __validate_more_comp_fields(
1710     p_rcv->p_log,
1711     p_mgrp,
1712     p_recvd_mcmember_rec,
1713     p_sa_mad->comp_mask) && __validate_port_caps(
1714     p_rcv->p_log,
1715     p_mgrp,
1716     p_physp) && (join_state != 0);
1717  
1718   if (! valid)
1719   {
1720     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1721              "osm_mcmr_rcv_join_mgrp: ERR 1B12: "
1722              "__validate_more_comp_fields, __validate_port_caps, "
1723              "or JoinState = 0 failed, "
1724              "sending IB_SA_MAD_STATUS_REQ_INVALID\n");
1725
1726     /* since we might have created the new group we need to cleanup */
1727     __cleanup_mgrp(p_rcv, mlid);
1728
1729     CL_PLOCK_RELEASE( p_rcv->p_lock );
1730
1731     sa_status  = IB_SA_MAD_STATUS_REQ_INVALID;
1732     osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1733     goto Exit;
1734   }
1735
1736   /*
1737    * Do some validation of the modification
1738    */
1739   if (! is_new_group)
1740   {
1741     /*
1742      * o15-0.2.1 requires validation of the requesting port
1743      * in the case of modification:
1744      */
1745     valid = __validate_modify(p_rcv,
1746                               p_mgrp,
1747                               osm_madw_get_mad_addr_ptr(p_madw),
1748                               p_recvd_mcmember_rec,
1749                               &p_mcmr_port);
1750     if (! valid)
1751     {
1752       osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1753                "osm_mcmr_rcv_join_mgrp: ERR 1B13: "
1754                "__validate_modify failed, "
1755                "sending IB_SA_MAD_STATUS_REQ_INVALID\n");
1756
1757       CL_PLOCK_RELEASE( p_rcv->p_lock );
1758
1759       sa_status   = IB_SA_MAD_STATUS_REQ_INVALID;
1760       osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1761       goto Exit;
1762     }
1763   }
1764
1765   /* create or update existing port (join-state will be updated) */
1766   status = __add_new_mgrp_port(
1767     p_rcv,
1768     p_mgrp,
1769     p_recvd_mcmember_rec,
1770     osm_madw_get_mad_addr_ptr(p_madw),
1771     &p_mcmr_port);
1772
1773   if (status != IB_SUCCESS)
1774   {
1775     /* we fail to add the port so we might need to delete the
1776        group */
1777     __cleanup_mgrp(p_rcv, mlid);
1778
1779     CL_PLOCK_RELEASE( p_rcv->p_lock );
1780     sa_status  = IB_SA_MAD_STATUS_NO_RESOURCES;
1781     osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1782     goto Exit;
1783   }
1784
1785   /* o15.0.1.11: copy the join state */
1786   mcmember_rec.scope_state = p_mcmr_port->scope_state;
1787
1788   /* copy qkey mlid tclass pkey sl_flow_hop mtu rate pkt_life sl_flow_hop */
1789   __copy_from_create_mc_rec(&mcmember_rec, &p_mgrp->mcmember_rec);
1790
1791   /* Release the lock as we don't need it. */
1792   CL_PLOCK_RELEASE( p_rcv->p_lock );
1793
1794   /* do the actual routing (actually schedule the update) */
1795   status =
1796     osm_sm_mcgrp_join(p_rcv->p_sm,
1797                       mlid,
1798                       p_recvd_mcmember_rec->port_gid.unicast.interface_id,
1799                       req_type );
1800
1801   if (status != IB_SUCCESS)
1802   {
1803     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1804              "osm_mcmr_rcv_join_mgrp: ERR 1B14: "
1805              "osm_sm_mcgrp_join failed, "
1806              "sending IB_SA_MAD_STATUS_NO_RESOURCES\n");
1807
1808     CL_PLOCK_EXCL_ACQUIRE(p_rcv->p_lock);
1809
1810     /* the request for routing failed so we need to remove the port */
1811     p_mgrp = __get_mgrp_by_mlid(p_rcv, mlid);
1812     if (p_mgrp != NULL)
1813     {
1814       osm_mgrp_remove_port(
1815         p_rcv->p_subn,
1816         p_rcv->p_log,
1817         p_mgrp,
1818         p_recvd_mcmember_rec->port_gid.unicast.interface_id);
1819       __cleanup_mgrp(p_rcv, mlid);
1820     }
1821     CL_PLOCK_RELEASE( p_rcv->p_lock );
1822     sa_status = IB_SA_MAD_STATUS_NO_RESOURCES;
1823     osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
1824     goto Exit;
1825
1826   } /* failed to route */
1827
1828   if ( osm_log_is_active( p_rcv->p_log, OSM_LOG_DEBUG ) )
1829     osm_dump_mc_record( p_rcv->p_log, &mcmember_rec, OSM_LOG_DEBUG );
1830
1831   __osm_mcmr_rcv_respond( p_rcv,
1832                           p_madw,
1833                           &mcmember_rec );
1834
1835  Exit:
1836   OSM_LOG_EXIT( p_rcv->p_log );
1837   return;
1838
1839 }
1840
1841 /**********************************************************************
1842  Add a patched multicast group to the results list
1843 **********************************************************************/
1844 static ib_api_status_t
1845 __osm_mcmr_rcv_new_mcmr(
1846   IN osm_mcmr_recv_t*         const p_rcv,
1847   IN const ib_member_rec_t*      p_rcvd_rec,
1848   IN cl_qlist_t*           const p_list)
1849 {
1850   osm_mcmr_item_t*            p_rec_item;
1851   ib_api_status_t          status = IB_SUCCESS;
1852
1853   OSM_LOG_ENTER( p_rcv->p_log, __osm_mcmr_rcv_new_mcmr );
1854
1855   p_rec_item = (osm_mcmr_item_t*)cl_qlock_pool_get( &p_rcv->pool );
1856   if( p_rec_item == NULL )
1857   {
1858     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
1859              "__osm_mcmr_rcv_new_mcmr: ERR 1B15: "
1860              "cl_qlock_pool_get failed\n" );
1861     status = IB_INSUFFICIENT_RESOURCES;
1862     goto Exit;
1863   }
1864
1865   cl_memclr( &p_rec_item->rec, sizeof( p_rec_item->rec ) );
1866
1867   /* HACK: Not trusted requesters should result with 0 Join
1868      State, Port Guid, and Proxy */
1869   p_rec_item->rec = *p_rcvd_rec;
1870   cl_qlist_insert_tail( p_list, (cl_list_item_t*)&p_rec_item->pool_item );
1871
1872  Exit:
1873   OSM_LOG_EXIT( p_rcv->p_log );
1874   return( status );
1875 }
1876
1877 /**********************************************************************
1878  Match the given mgrp to the requested mcmr
1879 **********************************************************************/
1880 void
1881 __osm_sa_mcm_by_comp_mask_cb(
1882   IN cl_map_item_t*     const p_map_item,
1883   IN void*              context )
1884 {
1885   const osm_mgrp_t *          const p_mgrp = (osm_mgrp_t *)p_map_item;
1886   osm_sa_mcmr_search_ctxt_t*  const p_ctxt =
1887     (osm_sa_mcmr_search_ctxt_t *)context;
1888   osm_mcmr_recv_t*            const p_rcv  = p_ctxt->p_rcv;
1889   const ib_member_rec_t*      p_rcvd_rec   = p_ctxt->p_mcmember_rec;
1890   const osm_physp_t*          p_req_physp  = p_ctxt->p_req_physp;
1891
1892   /* since we might change scope_state */
1893   ib_member_rec_t             match_rec;
1894   ib_net64_t                  comp_mask    = p_ctxt->comp_mask;
1895   osm_mcm_port_t*             p_mcm_port;
1896   ib_net64_t portguid = p_rcvd_rec->port_gid.unicast.interface_id;
1897   /* will be used for group or port info */
1898   uint8_t scope_state; 
1899   uint8_t scope_state_mask = 0;
1900
1901   OSM_LOG_ENTER( p_rcv->p_log, __osm_sa_mcm_by_comp_mask_cb );
1902
1903   osm_log(p_rcv->p_log, OSM_LOG_DEBUG,
1904           "__osm_sa_mcm_by_comp_mask_cb: "
1905           "Checking mlid:0x%X\n",
1906           cl_ntoh16(p_mgrp->mlid ));
1907
1908   /* the group might be marked for deletion */
1909   if (p_mgrp->to_be_deleted)
1910   {
1911     osm_log(p_rcv->p_log, OSM_LOG_DEBUG,
1912             "__osm_sa_mcm_by_comp_mask_cb: "
1913             "Group mlid:0x%X is marked to be deleted\n",
1914             cl_ntoh16(p_mgrp->mlid ));
1915     goto Exit;
1916   }
1917
1918   /* first try to eliminate the group by MGID, MLID, or P_Key */
1919   if ((IB_MCR_COMPMASK_MGID & comp_mask) &&
1920       cl_memcmp(&p_rcvd_rec->mgid, &p_mgrp->mcmember_rec.mgid, sizeof(ib_gid_t))) {
1921     goto Exit;
1922   }
1923
1924   if ((IB_MCR_COMPMASK_MLID & comp_mask) &&
1925       cl_memcmp(&p_rcvd_rec->mlid, &p_mgrp->mcmember_rec.mlid, sizeof(uint16_t))) {
1926     goto Exit;
1927   }
1928
1929   /* if the requester physical port doesn't have the pkey that is defined for the
1930      group - exit. */
1931   if (! osm_physp_has_pkey( p_rcv->p_log, p_mgrp->mcmember_rec.pkey, p_req_physp ))
1932     goto Exit;
1933
1934   /* so did we get the PortGUID mask */
1935   if (IB_MCR_COMPMASK_PORT_GID & comp_mask)
1936   {
1937     /* try to find this port */
1938     if (osm_mgrp_is_port_present(p_mgrp, portguid, &p_mcm_port))
1939     {
1940       scope_state = p_mcm_port->scope_state;
1941     }
1942     else
1943     {
1944       /* port not in group */
1945       goto Exit;
1946     }
1947   }
1948   else
1949   {
1950     /* point to the group information */
1951     scope_state = p_mgrp->mcmember_rec.scope_state;
1952   }
1953
1954   /* now do the rest of the match */
1955   if ((IB_MCR_COMPMASK_QKEY & comp_mask) &&
1956       (p_rcvd_rec->qkey != p_mgrp->mcmember_rec.qkey)) goto Exit;
1957
1958   if ((IB_MCR_COMPMASK_PKEY & comp_mask) &&
1959       (p_rcvd_rec->pkey != p_mgrp->mcmember_rec.pkey)) goto Exit;
1960
1961   if ((IB_MCR_COMPMASK_TCLASS & comp_mask) &&
1962       (p_rcvd_rec->tclass != p_mgrp->mcmember_rec.tclass)) goto Exit;
1963  
1964   /* check SL , Flow and Hop limit */
1965   {
1966     uint8_t mgrp_sl, query_sl;
1967     uint32_t mgrp_flow, query_flow;
1968     uint8_t mgrp_hop, query_hop;
1969
1970     ib_member_get_sl_flow_hop(p_rcvd_rec->sl_flow_hop,
1971                               &query_sl, &query_flow, &query_hop);
1972
1973     ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop,
1974                               &mgrp_sl, &mgrp_flow, &mgrp_hop);
1975
1976     if (IB_MCR_COMPMASK_SL & comp_mask )
1977       if (query_sl != mgrp_sl) goto Exit;
1978
1979     if (IB_MCR_COMPMASK_FLOW & comp_mask)
1980       if (query_flow != mgrp_flow) goto Exit;
1981
1982     if (IB_MCR_COMPMASK_HOP & comp_mask)
1983       if (query_hop != mgrp_hop) goto Exit;
1984   }
1985
1986   if (IB_MCR_COMPMASK_SCOPE & comp_mask)
1987     scope_state_mask = 0xF0;
1988
1989   if (IB_MCR_COMPMASK_JOIN_STATE & comp_mask)
1990     scope_state_mask = scope_state_mask | 0x0F;
1991
1992   if ((scope_state_mask & p_rcvd_rec->scope_state) !=
1993       (scope_state_mask & scope_state)) goto Exit;
1994
1995   if ((IB_MCR_COMPMASK_PROXY & comp_mask) &&
1996       (p_rcvd_rec->proxy_join != p_mgrp->mcmember_rec.proxy_join)) goto Exit;
1997
1998   /* need to validate mtu, rate, and pkt_lifetime fields. */
1999   if (__validate_more_comp_fields( p_rcv->p_log,
2000                                    p_mgrp,
2001                                    p_rcvd_rec,
2002                                    comp_mask ) == FALSE) goto Exit;
2003
2004   /* add to the list */
2005   match_rec = p_mgrp->mcmember_rec;
2006   match_rec.scope_state = scope_state;
2007
2008   __osm_mcmr_rcv_new_mcmr(p_rcv, &match_rec, p_ctxt->p_list);
2009  Exit:
2010   OSM_LOG_EXIT( p_rcv->p_log );
2011 }
2012
2013 /**********************************************************************
2014  Handle a query request
2015 **********************************************************************/
2016 void
2017 osm_mcmr_query_mgrp(IN osm_mcmr_recv_t*  const p_rcv,
2018                     IN const osm_madw_t* const p_madw) {
2019   const ib_sa_mad_t*          p_rcvd_mad;
2020   const ib_member_rec_t*   p_rcvd_rec;
2021   cl_qlist_t               rec_list;
2022   osm_madw_t*              p_resp_madw;
2023   ib_sa_mad_t*          p_resp_sa_mad;
2024   ib_member_rec_t*         p_resp_rec;
2025   uint32_t                 num_rec, pre_trim_num_rec;
2026 #ifndef VENDOR_RMPP_SUPPORT
2027   uint32_t                 trim_num_rec;
2028 #endif
2029   uint32_t                 i;
2030   osm_sa_mcmr_search_ctxt_t   context;
2031   osm_mcmr_item_t*         p_rec_item;
2032   ib_api_status_t       status;
2033   ib_net64_t               comp_mask;
2034   osm_physp_t*             p_req_physp;
2035   boolean_t                trusted_req = TRUE;
2036
2037   CL_ASSERT( p_rcv );
2038
2039   OSM_LOG_ENTER( p_rcv->p_log, osm_mcmr_query_mgrp );
2040
2041   CL_ASSERT( p_madw );
2042
2043   p_rcvd_mad = osm_madw_get_sa_mad_ptr( p_madw );
2044   p_rcvd_rec = (ib_member_rec_t*)ib_sa_mad_get_payload_ptr( p_rcvd_mad );
2045   comp_mask = p_rcvd_mad->comp_mask;
2046
2047   CL_ASSERT( p_rcvd_mad->attr_id == IB_MAD_ATTR_MCMEMBER_RECORD );
2048
2049   /* update the requester physical port. */
2050   p_req_physp = osm_get_physp_by_mad_addr(p_rcv->p_log,
2051                                           p_rcv->p_subn,
2052                                           osm_madw_get_mad_addr_ptr(p_madw) );
2053   if (p_req_physp == NULL)
2054   {
2055     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
2056              "osm_mcmr_query_mgrp: ERR 1B04: "
2057              "Cannot find requester physical port\n" );
2058     goto Exit;
2059   }
2060
2061   cl_qlist_init( &rec_list );
2062
2063   context.p_mcmember_rec = p_rcvd_rec;
2064   context.p_list = &rec_list;
2065   context.comp_mask = p_rcvd_mad->comp_mask;
2066   context.p_rcv = p_rcv;
2067   context.p_req_physp = p_req_physp;
2068
2069   CL_PLOCK_ACQUIRE( p_rcv->p_lock );
2070
2071   /* simply go over all MCGs and match */
2072   cl_qmap_apply_func( &p_rcv->p_subn->mgrp_mlid_tbl,
2073                       __osm_sa_mcm_by_comp_mask_cb,
2074                       &context);
2075
2076   CL_PLOCK_RELEASE( p_rcv->p_lock );
2077
2078   num_rec = cl_qlist_count( &rec_list );
2079
2080   /*
2081    * C15-0.1.30:
2082    * If we do a SubnAdmGet and got more than one record it is an error !
2083    */
2084   if ( (p_rcvd_mad->method == IB_MAD_METHOD_GET) &&
2085        (num_rec > 1)) {
2086     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
2087              "osm_mcmr_query_mgrp: ERR 1B05: "
2088              "Got more than one record for SubnAdmGet (%u)\n",
2089              num_rec );
2090     osm_sa_send_error( p_rcv->p_resp, p_madw,
2091                        IB_SA_MAD_STATUS_TOO_MANY_RECORDS);
2092
2093     /* need to set the mem free ... */
2094     p_rec_item = (osm_mcmr_item_t*)cl_qlist_remove_head( &rec_list );
2095     while( p_rec_item != (osm_mcmr_item_t*)cl_qlist_end( &rec_list ) )
2096     {
2097       cl_qlock_pool_put( &p_rcv->pool, &p_rec_item->pool_item );
2098       p_rec_item = (osm_mcmr_item_t*)cl_qlist_remove_head( &rec_list );
2099     }
2100
2101     goto Exit;
2102   }
2103
2104   pre_trim_num_rec = num_rec;
2105 #ifndef VENDOR_RMPP_SUPPORT
2106   trim_num_rec = (MAD_BLOCK_SIZE - IB_SA_MAD_HDR_SIZE) / sizeof(ib_member_rec_t);
2107   if (trim_num_rec < num_rec)
2108   {
2109     osm_log( p_rcv->p_log, OSM_LOG_VERBOSE,
2110              "osm_mcmr_query_mgrp: "
2111              "Number of records:%u trimmed to:%u to fit in one MAD\n",
2112              num_rec, trim_num_rec );
2113     num_rec = trim_num_rec;
2114   }
2115 #endif
2116
2117   osm_log( p_rcv->p_log, OSM_LOG_DEBUG,
2118            "osm_mcmr_query_mgrp: "
2119            "Returning %u records\n", num_rec );
2120
2121   if ((p_rcvd_mad->method == IB_MAD_METHOD_GET) && (num_rec == 0))
2122   {
2123     osm_sa_send_error( p_rcv->p_resp, p_madw,
2124                        IB_SA_MAD_STATUS_NO_RECORDS );
2125     goto Exit;
2126   }
2127
2128   /*
2129    * Get a MAD to reply. Address of Mad is in the received mad_wrapper
2130    */
2131   p_resp_madw = osm_mad_pool_get( p_rcv->p_mad_pool,
2132                                   p_madw->h_bind,
2133                                   num_rec * sizeof(ib_member_rec_t) + IB_SA_MAD_HDR_SIZE,
2134                                   osm_madw_get_mad_addr_ptr(p_madw) );
2135
2136   if( !p_resp_madw )
2137   {
2138     osm_log(p_rcv->p_log, OSM_LOG_ERROR,
2139             "osm_mcmr_query_mgrp: ERR 1B16: "
2140             "osm_mad_pool_get failed\n" );
2141
2142     for( i = 0; i < num_rec; i++ )
2143     {
2144       p_rec_item = (osm_mcmr_item_t*)cl_qlist_remove_head( &rec_list );
2145       cl_qlock_pool_put( &p_rcv->pool, &p_rec_item->pool_item );
2146     }
2147
2148     osm_sa_send_error( p_rcv->p_resp, p_madw,
2149                        IB_SA_MAD_STATUS_NO_RESOURCES );
2150
2151     goto Exit;
2152   }
2153
2154   p_resp_sa_mad = osm_madw_get_sa_mad_ptr( p_resp_madw );
2155
2156   /*
2157     Copy the MAD header back into the response mad.
2158     Set the 'R' bit and the payload length,
2159     Then copy all records from the list into the response payload.
2160   */
2161
2162   cl_memcpy( p_resp_sa_mad, p_rcvd_mad, IB_SA_MAD_HDR_SIZE );
2163   p_resp_sa_mad->method = (uint8_t)(p_resp_sa_mad->method | 0x80);
2164   /* C15-0.1.5 - always return SM_Key = 0 (table 151 p 782) */
2165   p_resp_sa_mad->sm_key = 0;
2166
2167   /* Fill in the offset (paylen will be done by the rmpp SAR) */
2168   p_resp_sa_mad->attr_offset =
2169     ib_get_attr_offset( sizeof(ib_member_rec_t) );
2170
2171 #ifndef VENDOR_RMPP_SUPPORT
2172   /* we support only one packet RMPP - so we will set the first and
2173      last flags for gettable */
2174   if (p_resp_sa_mad->method == IB_MAD_METHOD_GETTABLE_RESP)
2175   {
2176     p_resp_sa_mad->rmpp_type = IB_RMPP_TYPE_DATA;
2177     p_resp_sa_mad->rmpp_flags = IB_RMPP_FLAG_FIRST | IB_RMPP_FLAG_LAST | IB_RMPP_FLAG_ACTIVE;
2178   }
2179 #else
2180   /* forcefully define the packet as RMPP one */
2181   if (p_resp_sa_mad->method == IB_MAD_METHOD_GETTABLE_RESP)
2182     p_resp_sa_mad->rmpp_flags = IB_RMPP_FLAG_ACTIVE;
2183 #endif
2184
2185   p_resp_rec = (ib_member_rec_t*)ib_sa_mad_get_payload_ptr( p_resp_sa_mad );
2186
2187   /*
2188     p819 - The PortGID, JoinState and ProxyJoin shall be zero,
2189     except in the case of a trusted request.
2190     Note: In the mad controller we check that the SM_Key received on
2191     the mad is valid. Meaning - is either zero or equal to the local
2192     sm_key.
2193   */
2194   if (p_rcvd_mad->sm_key == 0)
2195     trusted_req = FALSE;
2196
2197   for ( i = 0; i < pre_trim_num_rec; i++ )
2198   {
2199     p_rec_item = (osm_mcmr_item_t*)cl_qlist_remove_head( &rec_list );
2200     /* copy only if not trimmed */
2201     if (i < num_rec)
2202     {
2203       *p_resp_rec = p_rec_item->rec;
2204       if (trusted_req == FALSE)
2205       {
2206         cl_memclr(&p_resp_rec->port_gid, sizeof(ib_gid_t));
2207         ib_member_set_join_state(p_resp_rec, 0);
2208         p_resp_rec->proxy_join = 0;
2209       }
2210     }
2211     cl_qlock_pool_put( &p_rcv->pool, &p_rec_item->pool_item );
2212     p_resp_rec++;
2213   }
2214
2215   CL_ASSERT( cl_is_qlist_empty( &rec_list ) );
2216
2217   status = osm_vendor_send( p_resp_madw->h_bind, p_resp_madw, FALSE );
2218   if(status != IB_SUCCESS)
2219   {
2220     osm_log(p_rcv->p_log, OSM_LOG_ERROR,
2221             "osm_mcmr_query_mgrp: ERR 1B17: "
2222             "osm_vendor_send. status = %s\n",
2223             ib_get_err_str(status));
2224     goto Exit;
2225   }
2226
2227  Exit:
2228   OSM_LOG_EXIT( p_rcv->p_log );
2229 }
2230
2231 /**********************************************************************
2232  **********************************************************************/
2233 void
2234 osm_mcmr_rcv_process(
2235   IN osm_mcmr_recv_t* const p_rcv,
2236   const IN osm_madw_t* const p_madw )
2237 {
2238   ib_sa_mad_t *p_sa_mad;
2239   ib_net16_t sa_status = IB_SA_MAD_STATUS_REQ_INVALID;
2240   ib_member_rec_t *p_recvd_mcmember_rec;
2241   boolean_t valid;
2242
2243   CL_ASSERT( p_rcv );
2244
2245   OSM_LOG_ENTER( p_rcv->p_log, osm_mcmr_rcv_process );
2246
2247   CL_ASSERT( p_madw );
2248
2249   p_sa_mad = osm_madw_get_sa_mad_ptr( p_madw );
2250   p_recvd_mcmember_rec =
2251     (ib_member_rec_t*)ib_sa_mad_get_payload_ptr( p_sa_mad );
2252
2253   switch (p_sa_mad->method)
2254   {
2255   case IB_MAD_METHOD_SET:
2256     valid = __check_join_comp_mask(p_sa_mad->comp_mask);
2257     if(!valid)
2258     {
2259       osm_log( p_rcv->p_log, OSM_LOG_ERROR,
2260                "osm_mcmr_rcv_process: ERR 1B18: "
2261                "component mask = 0x%016" PRIx64 ", "
2262                "expected comp mask = 0x%016" PRIx64 " ,"
2263                "MGID: 0x%016" PRIx64 " : "
2264                "0x%016" PRIx64 " for "
2265                "PortGID: 0x%016" PRIx64 " : "
2266                "0x%016" PRIx64 "\n",
2267                cl_ntoh64(p_sa_mad->comp_mask),
2268                CL_NTOH64(JOIN_MC_COMP_MASK),
2269                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.prefix ),
2270                cl_ntoh64( p_recvd_mcmember_rec->mgid.unicast.interface_id ),
2271                cl_ntoh64( p_recvd_mcmember_rec->port_gid.unicast.prefix ),
2272                cl_ntoh64( p_recvd_mcmember_rec->port_gid.unicast.interface_id ) );
2273
2274       osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
2275       goto Exit;
2276     }
2277
2278     /*
2279      * Join or Create Multicast Group
2280      */
2281     osm_mcmr_rcv_join_mgrp(p_rcv, p_madw);
2282     break;
2283   case IB_MAD_METHOD_DELETE:
2284     valid = __check_join_comp_mask(p_sa_mad->comp_mask);
2285     if(!valid)
2286     {
2287       osm_log( p_rcv->p_log, OSM_LOG_ERROR,
2288                "osm_mcmr_rcv_process: ERR 1B20: "
2289                "component mask = 0x%016" PRIx64 ", "
2290                "expected comp mask = 0x%016" PRIx64 "\n",
2291                cl_ntoh64(p_sa_mad->comp_mask),
2292                CL_NTOH64(JOIN_MC_COMP_MASK));
2293
2294       osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
2295       goto Exit;
2296     }
2297
2298     /*
2299      * Leave Multicast Group
2300      */
2301     osm_mcmr_rcv_leave_mgrp(p_rcv, p_madw);
2302     break;
2303   case IB_MAD_METHOD_GET:
2304   case IB_MAD_METHOD_GETTABLE:
2305     /*
2306      * Querying a Multicast Group
2307      */
2308     osm_mcmr_query_mgrp(p_rcv, p_madw);
2309     break;
2310   default:
2311     osm_log( p_rcv->p_log, OSM_LOG_ERROR,
2312              "osm_mcmr_rcv_process: ERR 1B21: "
2313              "Unsupported Method (%s)\n",
2314              ib_get_sa_method_str( p_sa_mad->method ) );
2315     osm_sa_send_error( p_rcv->p_resp, p_madw, sa_status);
2316     break;
2317   }
2318
2319  Exit:
2320   OSM_LOG_EXIT( p_rcv->p_log );
2321   return;
2322
2323 }