1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME
80**
81**      rpclist.c
82**
83**  FACILITY:
84**
85**      Remote Procedure Call (RPC)
86**
87**  ABSTRACT:
88**
89**  This module contains routines to maintain lookaside list descriptors.
90**
91**
92*/
93
94#include <commonp.h>
95
96GLOBAL rpc_lookaside_rcb_t rpc_g_lookaside_rcb =
97{
98    RPC_C_LOOKASIDE_RES,
99    0,
100    RPC_C_LOOKASIDE_RES_MAX_WAIT,
101    RPC_C_LOOKASIDE_RES_WAIT,
102	 RPC_MUTEX_INITIALIZER,
103	 RPC_COND_INITIALIZER
104};
105
106/*
107**++
108**
109**  ROUTINE NAME:       rpc__list_desc_init
110**
111**  SCOPE:              PRIVATE - declared in rpclist.h
112**
113**  DESCRIPTION:
114**
115**  Initializes a lookaside list descriptor.  The maximum size and element
116**  size are set as part of this operation.
117**
118**  INPUTS:
119**
120**      list_desc       List descriptor which is to be initialized.
121**
122**      max_size        The maximum length of the lookaside list.
123**
124**      element_size    The size of each list element.
125**
126**      element_type    The type of each list element (from rpcmem.h).
127**
128**      alloc_rtn       The element specific routine to be
129**                      called when allocating from heap. If this is
130**                      NULL no routine will be called.
131**
132**      free_rtn        The element specific alloc routine to be
133**                      called when freeing to heap. If this is NULL
134**                      no routine will be called.
135**
136**      mutex           The list specific mutex used to protect the
137**                      integrity of the list. If the NULL is
138**                      provided the global lookaside list mutex and
139**                      condition variable will be used.It is used when
140**                      blocking on or signalling condition
141**                      variables. Note that the neither
142**                      rpc__list_element_alloc or _free ever
143**                      explicitly acquires or releases this mutex.
144**                      This must be done by the caller.
145**
146**      cond            The list specific condition variable
147**                      associated with the above mutex. Valid iff
148**                      mutex is not NULL.
149**
150**  INPUTS/OUTPUTS:     none
151**
152**  OUTPUTS:            none
153**
154**  IMPLICIT INPUTS:    none
155**
156**  IMPLICIT OUTPUTS:   none
157**
158**  FUNCTION VALUE:     none
159**
160**  SIDE EFFECTS:       none
161**
162**--
163**/
164
165PRIVATE void rpc__list_desc_init
166(
167    rpc_list_desc_p_t               list_desc,
168    unsigned32                      max_size,
169    unsigned32                      element_size,
170    unsigned32                      element_type,
171    rpc_list_element_alloc_fn_t     alloc_rtn,
172    rpc_list_element_free_fn_t      free_rtn,
173    rpc_mutex_p_t                   mutex,
174    rpc_cond_p_t                    cond
175)
176{
177    list_desc->max_size = max_size;
178    list_desc->cur_size = 0;
179    list_desc->element_size = element_size;
180    list_desc->element_type = element_type;
181    list_desc->alloc_rtn = alloc_rtn;
182    list_desc->free_rtn = free_rtn;
183    if (mutex == NULL)
184    {
185        list_desc->use_global_mutex = true;
186    }
187    else
188    {
189        list_desc->use_global_mutex = false;
190        list_desc->mutex = mutex;
191        list_desc->cond = cond;
192    }
193    RPC_LIST_INIT (list_desc->list_head);
194}
195
196/*
197**++
198**
199**  ROUTINE NAME:       rpc__list_element_alloc
200**
201**  SCOPE:              PRIVATE - declared in rpclist.h
202**
203**  DESCRIPTION:
204**
205**  Remove the first element in the lookaside list and return a pointer
206**  to it.  If the lookaside list is empty, an element of the size
207**  indicated in the lookaside list descriptor will be allocated from
208**  the heap.
209**
210**  INPUTS:             none
211**
212**      list_desc       The lookaside list descriptor.
213**
214**      block           true if the alloc should block for heap to become free
215**                      false if it is not to block
216**
217**  INPUTS/OUTPUTS:     none
218**
219**  OUTPUTS:            none
220**
221**  IMPLICIT INPUTS:    none
222**
223**  IMPLICIT OUTPUTS:   none
224**
225**  FUNCTION VALUE:
226**
227**      return          Pointer to the allocated list element.
228**
229**  SIDE EFFECTS:       none
230**
231**--
232**/
233
234PRIVATE dce_pointer_t rpc__list_element_alloc
235(
236    rpc_list_desc_p_t       list_desc,
237    boolean32               block
238)
239{
240    volatile dce_pointer_t  element = NULL;
241    unsigned32          wait_cnt;
242    struct timespec     delta;
243    struct timespec     abstime;
244
245    RPC_LOG_LIST_ELT_ALLOC_NTR;
246
247    for (wait_cnt = 0;
248         wait_cnt < rpc_g_lookaside_rcb.max_wait_times;
249         wait_cnt++)
250    {
251        /*
252         * Acquire the global resource control lock for all lookaside
253         * lists if the caller doesn't have their own lock.
254         */
255        if (list_desc->use_global_mutex)
256        {
257            RPC_MUTEX_LOCK (rpc_g_lookaside_rcb.res_lock);
258        }
259
260        /*
261         * Try allocating a structure off the lookaside list given.
262         */
263        if (list_desc->cur_size > 0)
264        {
265#define DEBUG 1
266#ifdef DEBUG
267            if (list_desc->list_head.next == NULL)
268            {
269                /*
270                 * rpc_m_lookaside_corrupt
271                 * "(%s) Lookaside list is corrupted"
272                 */
273                rpc_dce_svc_printf (
274                    __FILE__, __LINE__,
275                    "%s",
276                    rpc_svc_general,
277                    svc_c_sev_fatal | svc_c_action_abort,
278                    rpc_m_lookaside_corrupt,
279                    "rpc__list_element_alloc" );
280            }
281#endif
282            list_desc->cur_size--;
283            RPC_LIST_REMOVE_HEAD (list_desc->list_head, element, dce_pointer_t);
284
285            /*
286             * Release the global resource control lock for all lookaside
287             * lists if the caller doesn't have their own lock.
288             */
289            if (list_desc->use_global_mutex)
290            {
291                RPC_MUTEX_UNLOCK (rpc_g_lookaside_rcb.res_lock);
292            }
293            break;
294        }
295        else
296        {
297            /*
298             * Release the global resource control lock if the
299             * caller doesn't have their own lock for all lookaside lists
300             * since the structure was available on the lookaside list.
301             *
302             * We do it now because allocating an element from heap is a relatively
303             * time consuming operation.
304             */
305            if (list_desc->use_global_mutex)
306            {
307                RPC_MUTEX_UNLOCK (rpc_g_lookaside_rcb.res_lock);
308            }
309
310            /*
311             * The lookaside list is empty. Try and allocate from
312             * heap.
313             */
314            RPC_MEM_ALLOC (element,
315                           dce_pointer_t,
316                           list_desc->element_size,
317                           list_desc->element_type,
318                           RPC_C_MEM_NOWAIT);
319
320            if (element == NULL)
321            {
322                /*
323                 * The heap allocate failed. If the caller indicated
324                 * that we should not block return right now.
325                 */
326                if (block == false)
327                {
328                    break;
329                }
330
331                delta.tv_sec = rpc_g_lookaside_rcb.wait_time;
332                delta.tv_nsec = 0;
333                dcethread_get_expiration (&delta, &abstime);
334
335                /*
336                 * If we are using the global lookaside list lock
337                 * then reaquire the global lookaside list lock and
338                 * wait on the global lookaside list condition
339                 * variable otherwise use the caller's mutex and
340                 * condition variable.
341                 */
342                if (list_desc->use_global_mutex)
343                {
344                    RPC_MUTEX_LOCK (rpc_g_lookaside_rcb.res_lock);
345                    RPC_COND_TIMED_WAIT (rpc_g_lookaside_rcb.wait_flg,
346                                         rpc_g_lookaside_rcb.res_lock,
347                                         &abstime);
348                    RPC_MUTEX_UNLOCK (rpc_g_lookaside_rcb.res_lock);
349                }
350                else
351                {
352                    RPC_COND_TIMED_WAIT (*list_desc->cond,
353                                         *list_desc->mutex,
354                                         &abstime);
355                }
356
357                /*
358                 * Try to allocate the structure again.
359                 */
360                continue;
361            }
362            else
363            {
364                /*
365                 * The RPC_MEM_ALLOC succeeded. If an alloc routine
366                 * was specified when the lookaside list was inited
367                 * call it now.
368                 */
369                if (list_desc->alloc_rtn != NULL)
370                {
371                    /*
372                     * Catch any exceptions which may occur in the
373                     * list-specific alloc routine. Any exceptions
374                     * will be caught and the memory will be freed.
375                     */
376                    DCETHREAD_TRY
377                    {
378                        (*list_desc->alloc_rtn) (element);
379                    }
380                    DCETHREAD_CATCH_ALL(THIS_CATCH)
381                    {
382                        RPC_MEM_FREE (element, list_desc->element_type);
383                        element = NULL;
384                        /*
385                         * rpc_m_call_failed_no_status
386                         * "%s failed"
387                         */
388                        rpc_dce_svc_printf (
389                            __FILE__, __LINE__,
390                            "%s",
391                            rpc_svc_general,
392                            svc_c_sev_fatal | svc_c_action_abort,
393                            rpc_m_call_failed_no_status,
394                            "rpc__list_element_alloc/(*list_desc->alloc_rtn)(element)" );
395                    }
396                    DCETHREAD_ENDTRY
397                }
398                break;
399            }
400        }
401    }
402    if (element != NULL) {
403        ((rpc_list_p_t)element)->next = NULL;
404        ((rpc_list_p_t)element)->last = NULL;
405    }
406    RPC_LOG_LIST_ELT_ALLOC_XIT;
407    return (element);
408}
409
410
411/*
412**++
413**
414**  ROUTINE NAME:       rpc__list_desc_free
415**
416**  SCOPE:              PRIVATE - declared in rpclist.h
417**
418**  DESCRIPTION:
419**
420**  Returns an element to the lookaside list.  If this would result
421**  in the current size of the lookaside list becoming greater than
422**  the maximum size, the element will be returned to the heap instead.
423**
424**  INPUTS:
425**
426**      list_desc       The list descriptor.
427**
428**      list_element    Pointer to the element to be freed.
429**
430**  INPUTS/OUTPUTS:     none
431**
432**  OUTPUTS:            none
433**
434**  IMPLICIT INPUTS:    none
435**
436**  IMPLICIT OUTPUTS:   none
437**
438**  FUNCTION VALUE:     none
439**
440**  SIDE EFFECTS:       none
441**
442**--
443**/
444
445PRIVATE void rpc__list_element_free
446(
447    rpc_list_desc_p_t       list_desc,
448    dce_pointer_t               list_element
449)
450{
451    RPC_LOG_LIST_ELT_FREE_NTR;
452
453    assert(list_desc != NULL);
454    assert(list_element != NULL);
455
456    /*
457     * Acquire the global resource control lock for all lookaside
458     * lists if the caller doesn't have their own lock.
459     */
460    if (list_desc->use_global_mutex)
461    {
462        RPC_MUTEX_LOCK (rpc_g_lookaside_rcb.res_lock);
463    }
464
465    if (list_desc->cur_size < list_desc->max_size)
466    {
467        list_desc->cur_size++;
468
469        RPC_LIST_ADD_TAIL (list_desc->list_head, list_element, dce_pointer_t);
470
471        /*
472         * Now check whether any other thread is waiting for a lookaside list
473         * structure.
474         */
475        if (rpc_g_lookaside_rcb.waiter_cnt > 0)
476        {
477            /*
478             * There are waiters. Signal the global lookaside list
479             * condition variable if the caller doesn't have one.
480             * Otherwise signal the caller provided condition
481             * variable.
482             */
483            if (list_desc->use_global_mutex)
484            {
485                RPC_COND_SIGNAL (rpc_g_lookaside_rcb.wait_flg,
486                                 rpc_g_lookaside_rcb.res_lock);
487            }
488            else
489            {
490                RPC_COND_SIGNAL (*list_desc->cond,
491                                 *list_desc->mutex);
492            }
493        }
494
495        /*
496         * Release the global resource control lock for all lookaside
497         * lists if the caller doesn't have their own lock
498         * since the structure has now been added to the list.
499         */
500        if (list_desc->use_global_mutex)
501        {
502            RPC_MUTEX_UNLOCK (rpc_g_lookaside_rcb.res_lock);
503        }
504    }
505    else
506    {
507        /*
508         * If a free routine was specified when this lookaside list
509         * was inited call it now.
510         */
511        if (list_desc->free_rtn != NULL)
512        {
513            (list_desc->free_rtn) (list_element);
514        }
515
516        /*
517         * Release the global resource control lock for all
518         * lookaside lists if the caller doesn't have their own lock.
519         *
520         * We do it now because freeing an element to the heap is a relatively
521         * time consuming operation.
522         */
523        if (list_desc->use_global_mutex)
524        {
525            RPC_MUTEX_UNLOCK (rpc_g_lookaside_rcb.res_lock);
526        }
527
528	memset (list_element, 0, list_desc->element_size);
529        RPC_MEM_FREE (list_element, list_desc->element_type);
530    }
531
532    RPC_LOG_LIST_ELT_FREE_XIT;
533}
534
535/*
536**++
537**
538**  ROUTINE NAME:       rpc__list_fork_handler
539**
540**  SCOPE:              PRIVATE - declared in rpclist.h
541**
542**  DESCRIPTION:
543**
544**  Perform fork-related processing, depending on what stage of the
545**  fork we are currently in.
546**
547**  INPUTS:
548**
549**      stage           The current stage of the fork process.
550**
551**  INPUTS/OUTPUTS:     none
552**
553**  OUTPUTS:            none
554**
555**  IMPLICIT INPUTS:    none
556**
557**  IMPLICIT OUTPUTS:   none
558**
559**  FUNCTION VALUE:     none
560**
561**  SIDE EFFECTS:       none
562**
563**--
564**/
565
566PRIVATE void rpc__list_fork_handler
567(
568    rpc_fork_stage_id_t     stage
569)
570{
571    switch ((int)stage)
572    {
573        case RPC_C_PREFORK:
574                break;
575        case RPC_C_POSTFORK_PARENT:
576                break;
577        case RPC_C_POSTFORK_CHILD:
578                /*
579                 * Reset the lookaside waiter's count.
580                 */
581                rpc_g_lookaside_rcb.waiter_cnt = 0;
582                break;
583    }
584}
585