openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

l2notify.c (7252B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * File:        l2notify.c
      8  * Purpose:     L2 table change notification, async task
      9  * Requires:
     10  */
     11 
     12 #include <sal/core/libc.h>
     13 #include <shared/alloc.h>
     14 
     15 #include <bcm/types.h>
     16 
     17 #include <bcmx/l2.h>
     18 
     19 #include "bcmx_int.h"
     20 
     21 /****************************************************************
     22  *
     23  * BCMX L2 address change notification
     24  *
     25  *    Works with the BCM dispatch layer.
     26  *
     27  *    Maintains a list of callbacks locally.  Global indication of
     28  *    whether BCMX notify is active is used to determine if
     29  *    bcm_l2_addr_register is called when new devices are attached.
     30  *
     31  *    Semantics for callbacks are the same, except that a bcmx_l2_addr_t
     32  *    is used.
     33  *
     34  *    L2 notify configuration is locked during callbacks, so
     35  *    application MUST not make register/unregister calls from
     36  *    within callbacks.
     37  *
     38  *    Assumes the lower layers are initialized elsewhere.
     39  */
     40 
     41 #define BCMX_L2_NOTIFY_SET_ERROR_CHECK(_unit, _check, _rv)    \
     42     BCMX_SET_ERROR_CHECK(_unit, _check, _rv)
     43 
     44 #define BCMX_L2_NOTIFY_DELETE_ERROR_CHECK(_unit, _check, _rv)    \
     45     BCMX_DELETE_ERROR_CHECK(_unit, _check, _rv)
     46 
     47 typedef struct l2n_handler_s {
     48     struct l2n_handler_s        *next;
     49     bcmx_l2_notify_f             lh_f;
     50     void                        *userdata;
     51 } l2n_handler_t;
     52 
     53 static sal_mutex_t _l2n_lock;
     54 static l2n_handler_t *handler_list;
     55 
     56 #define CHECK_INIT    do {                                             \
     57     BCMX_READY_CHECK;                                                  \
     58     if (_l2n_lock == NULL &&                                           \
     59         ((_l2n_lock = sal_mutex_create("bcmx_l2_notify")) == NULL))    \
     60         return BCM_E_MEMORY;                                           \
     61     } while (0)
     62 
     63 #define L2N_LOCK sal_mutex_take(_l2n_lock, sal_mutex_FOREVER)
     64 #define L2N_UNLOCK sal_mutex_give(_l2n_lock)
     65 
     66 /* The callback registered with the BCM layer */
     67 
     68 STATIC void
     69 _bcm_l2_addr_cb(int unit, bcm_l2_addr_t *l2addr, int insert, void *userdata)
     70 {
     71     bcmx_l2_addr_t bcmx_l2;
     72     l2n_handler_t *handler;
     73 
     74     COMPILER_REFERENCE(userdata);
     75 
     76     if (handler_list == NULL || _l2n_lock == NULL) {
     77         return;
     78     }
     79     sal_memset(&bcmx_l2, 0, sizeof(bcmx_l2));
     80     bcmx_l2_addr_from_bcm(&bcmx_l2, NULL, l2addr);
     81     bcmx_l2.bcm_unit = unit;
     82 
     83     L2N_LOCK;
     84     handler = handler_list;
     85     while (handler != NULL) {
     86         (handler->lh_f)(&bcmx_l2, insert, handler->userdata);
     87         handler = handler->next;
     88     }
     89     L2N_UNLOCK;
     90 }
     91 
     92 
     93 /* Search for a handler in our list; assumes config lock held */
     94 
     95 static INLINE l2n_handler_t *
     96 l2n_handler_find(bcmx_l2_notify_f cb, void *userdata, l2n_handler_t **prev)
     97 {
     98     l2n_handler_t *handler = handler_list;
     99 
    100     if (prev != NULL) {
    101         *prev = NULL;
    102     }
    103     while (handler != NULL) {
    104         if ((handler->lh_f == cb) && (userdata == handler->userdata)) {
    105             return handler;
    106         }
    107         if (prev != NULL) {
    108             *prev = handler;
    109         }
    110         handler = handler->next;
    111     }
    112 
    113     return NULL;
    114 }
    115 
    116 /* Force registration to rlink layer */
    117 STATIC int
    118 _bcmx_l2_rlink_register(void)
    119 {
    120     int rv = BCM_E_UNAVAIL, tmp_rv;
    121     int bcm_unit, i;
    122 
    123     BCMX_UNIT_ITER(bcm_unit, i) {
    124         tmp_rv = bcm_l2_addr_register(bcm_unit, _bcm_l2_addr_cb, NULL);
    125         BCM_IF_ERROR_RETURN
    126             (BCMX_L2_NOTIFY_SET_ERROR_CHECK(bcm_unit, tmp_rv, &rv));
    127     }
    128 
    129     return rv;
    130 }
    131 
    132 
    133 /*
    134  * Function:
    135  *      bcmx_l2_notify_register
    136  */
    137 
    138 int
    139 bcmx_l2_notify_register(bcmx_l2_notify_f callback, void *userdata)
    140 {
    141     l2n_handler_t *handler;
    142     int rv = BCM_E_NONE;
    143 
    144     CHECK_INIT;
    145 
    146     L2N_LOCK;
    147 
    148     handler = l2n_handler_find(callback, userdata, NULL);
    149     if (handler == NULL) {  /* Not there */
    150         handler = sal_alloc(sizeof(l2n_handler_t), "bcmx_l2_addr_reg");
    151         if (handler == NULL) {
    152             rv = BCM_E_MEMORY;
    153         } else {
    154             handler->lh_f = callback;
    155             handler->userdata = userdata;
    156 
    157             handler->next = handler_list;
    158             handler_list = handler;
    159         }
    160     }
    161 
    162     L2N_UNLOCK;
    163 
    164     if (handler != NULL) {  /* Always reregister w/ rlink */
    165         (void)_bcmx_l2_rlink_register();
    166     }
    167 
    168     return rv;
    169 }
    170 
    171 
    172 /*
    173  * Function:
    174  *      bcmx_l2_notify_unregister
    175  */
    176 
    177 int
    178 bcmx_l2_notify_unregister(bcmx_l2_notify_f callback, void * userdata)
    179 {
    180     int rv = BCM_E_UNAVAIL, tmp_rv;
    181     int i, bcm_unit;
    182     l2n_handler_t *prev, *handler;
    183     int unreg = FALSE;  /* Should we unregister? (No callbacks registered). */
    184 
    185     CHECK_INIT;
    186 
    187     L2N_LOCK;
    188 
    189     handler = l2n_handler_find(callback, userdata, &prev);
    190     if (handler == NULL) {  /* Wasn't there */
    191         L2N_UNLOCK;
    192         return BCM_E_NONE;
    193     }
    194 
    195     if (prev != NULL) {
    196         prev->next = handler->next;
    197     } else { /* First thing in list */
    198         handler_list = handler->next;
    199     }
    200     sal_free(handler);
    201 
    202     if (handler_list == NULL) {  /* Nothing registered */
    203         unreg = TRUE;
    204     }
    205 
    206     L2N_UNLOCK;
    207 
    208     if (unreg) {
    209         BCMX_UNIT_ITER(bcm_unit, i) {
    210             tmp_rv = bcm_l2_addr_unregister(bcm_unit,
    211                                             _bcm_l2_addr_cb,
    212                                             NULL);
    213         BCM_IF_ERROR_RETURN
    214             (BCMX_L2_NOTIFY_DELETE_ERROR_CHECK(bcm_unit, tmp_rv, &rv));
    215         }
    216     }
    217 
    218     return rv;
    219 }
    220 
    221 
    222 /*
    223  * Function:
    224  *      bcmx_l2_notify_start
    225  * Purpose:
    226  *      Start L2 notify
    227  * Returns:
    228  *      BCM_E_XXX
    229  * Notes:
    230  *      Just registers with BCM layer to get messages.
    231  */
    232 
    233 int
    234 bcmx_l2_notify_start(void)
    235 {
    236     int rv;
    237 
    238     BCMX_READY_CHECK;
    239 
    240     rv = _bcmx_l2_rlink_register();
    241 
    242     return rv;
    243 }
    244 
    245 
    246 /*
    247  * Function:
    248  *      bcmx_l2_notify_stop
    249  * Purpose:
    250  *      Stop L2 notification at BCMX layer
    251  * Returns:
    252  *      BCM_E_XXX
    253  * Notes:
    254  *      Unregisters with BCM layer for all units; Does nothing to
    255  *      lower layers.
    256  */
    257 
    258 int
    259 bcmx_l2_notify_stop(void)
    260 {
    261     int rv = BCM_E_UNAVAIL, tmp_rv;
    262     int i, bcm_unit;
    263 
    264     BCMX_READY_CHECK;
    265 
    266     BCMX_UNIT_ITER(bcm_unit, i) {
    267         tmp_rv = bcm_l2_addr_unregister(bcm_unit, _bcm_l2_addr_cb, NULL);
    268         BCM_IF_ERROR_RETURN
    269             (BCMX_L2_NOTIFY_DELETE_ERROR_CHECK(bcm_unit, tmp_rv, &rv));
    270     }
    271 
    272     return rv;
    273 }
    274 
    275 
    276 /*
    277  * Function:
    278  *      bcmx_l2_device_add
    279  * Purpose:
    280  *      Add a device to the L2 notification list
    281  * Parameters:
    282  *      bcm_unit     - Unit being added
    283  * Returns:
    284  *      BCM_E_XXX
    285  * Notes:
    286  *      Only matters if currently running.  No local state, just
    287  *      register BCMX callback with BCM layer.
    288  */
    289 
    290 int
    291 bcmx_l2_device_add(int bcm_unit)
    292 {
    293     int rv = BCM_E_NONE;
    294     int reg = FALSE;
    295 
    296     CHECK_INIT;
    297 
    298     /* If any registered callbacks, make sure registered on this device */
    299     L2N_LOCK; /* Make sure the handler_list is not being updated */
    300     if (handler_list != NULL) {
    301         reg = TRUE;
    302     }
    303     L2N_UNLOCK;
    304 
    305     if (reg) {
    306         rv = bcm_l2_addr_register(bcm_unit, _bcm_l2_addr_cb, NULL);
    307     }
    308 
    309     return rv;
    310 }
    311 
    312 int
    313 bcmx_l2_device_remove(int bcm_unit)
    314 {
    315     BCMX_READY_CHECK;
    316 
    317     return bcm_l2_addr_unregister(bcm_unit, _bcm_l2_addr_cb, NULL);
    318 }