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 }