link.c (11521B)
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: link.c 8 * Purpose: 9 * Requires: 10 */ 11 12 #include <shared/bsl.h> 13 14 #include <shared/alloc.h> 15 #include <sal/core/sync.h> 16 17 #include <bcm/types.h> 18 #include <bcm/port.h> 19 #include <bcm/link.h> 20 21 #include <bcmx/bcmx.h> 22 #include <bcmx/link.h> 23 #include <soc/mem.h> 24 25 #include "bcmx_int.h" 26 27 #define BCMX_LINK_SET_ERROR_CHECK(_unit, _check, _rv) \ 28 BCMX_SET_ERROR_CHECK(_unit, _check, _rv) 29 30 #define BCMX_LINK_GET_IS_VALID(_unit, _rv) \ 31 BCMX_ERROR_IS_VALID(_unit, _rv) 32 33 typedef struct lsx_handler_s { 34 struct lsx_handler_s *lh_next; 35 bcmx_link_notify_f lh_f; 36 } lsx_handler_t; 37 38 static sal_mutex_t lsx_lock; /* Synchronization */ 39 static lsx_handler_t *lsx_handler; /* List of handlers */ 40 41 /* lsx_interval < 0 implies it hasn't been set yet */ 42 static int lsx_interval = -1; /* STATUS ONLY */ 43 44 #define LSX_CHECK_INIT do { \ 45 BCMX_READY_CHECK; \ 46 if (lsx_lock == NULL) \ 47 BCM_IF_ERROR_RETURN(_bcmx_lsx_init()); \ 48 } while (0) 49 50 #define LSX_LOCK sal_mutex_take(lsx_lock, sal_mutex_FOREVER) 51 #define LSX_UNLOCK sal_mutex_give(lsx_lock) 52 53 STATIC int 54 _bcmx_lsx_init(void) 55 { 56 lsx_lock = sal_mutex_create("bcmx_l2_notify"); 57 if (lsx_lock == NULL) { 58 return BCM_E_MEMORY; 59 } 60 61 return BCM_E_NONE; 62 } 63 64 65 /* Callback registered with BCM layer */ 66 STATIC void 67 bcmx_bcm_ls_cb(int unit, bcm_port_t port, bcm_port_info_t *info) 68 { 69 bcmx_lport_t lport; 70 lsx_handler_t *cb; /* List of handlers */ 71 72 LOG_VERBOSE(BSL_LS_BCMX_LINK, 73 (BSL_META_U(unit, 74 "BCMX LS callback, u %d. p %d\n"), unit, port)); 75 76 if (lsx_lock == NULL || lsx_handler == NULL) { 77 return; 78 } 79 80 /* Translate unit/port into lport */ 81 if (BCM_FAILURE(_bcmx_dest_from_unit_port(&lport, unit, port, 82 BCMX_DEST_CONVERT_DEFAULT))) { 83 return; 84 } 85 86 LSX_LOCK; 87 cb = lsx_handler; 88 while (cb != NULL) { 89 cb->lh_f(lport, info); 90 cb = cb->lh_next; 91 } 92 LSX_UNLOCK; 93 } 94 95 /* Make connection to BCM layer for all known units. */ 96 97 STATIC int 98 _bcmx_linkscan_rlink_register(void) 99 { 100 int bcm_unit, i; 101 int rv = BCM_E_UNAVAIL, tmp_rv; 102 103 BCMX_UNIT_ITER(bcm_unit, i) { 104 tmp_rv = bcm_linkscan_register(bcm_unit, bcmx_bcm_ls_cb); 105 BCM_IF_ERROR_RETURN(BCMX_LINK_SET_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 106 } 107 108 return rv; 109 } 110 111 /* 112 * Function: 113 * bcmx_linkscan_register/unregister 114 * Purpose: 115 * Register/unregister a BCMX level link change notification callback 116 * Parameters: 117 * ln_cb - The callback function pointer 118 * Returns: 119 * BCM_E_XXX 120 * Notes: 121 * Register may be called multiple times without burning more 122 * registration entries. In addition, it always calls down to 123 * the lower layer to make sure registration exists on all 124 * currently known units. In this way, after more units are 125 * added, call this again to ensure connections. 126 */ 127 128 int 129 bcmx_linkscan_register(bcmx_link_notify_f ln_cb) 130 { 131 lsx_handler_t *cb, *last; 132 int registered = FALSE; /* Successfully registered locally? */ 133 int rv = BCM_E_NONE; 134 135 LSX_CHECK_INIT; 136 137 LOG_VERBOSE(BSL_LS_BCMX_LINK, 138 (BSL_META("BCMX LS reg, %p\n"), 139 ln_cb)); 140 141 LSX_LOCK; 142 last = NULL; 143 cb = lsx_handler; 144 while (cb != NULL) { 145 if (cb->lh_f == ln_cb) { 146 registered = TRUE; 147 break; 148 } 149 last = cb; 150 cb = cb->lh_next; 151 } 152 153 if (!registered) { /* Not found yet; add to list */ 154 cb = sal_alloc(sizeof(lsx_handler_t), "bcmx_ls_reg"); 155 if (cb != NULL) { 156 157 /* Put into end of list */ 158 cb->lh_f = ln_cb; 159 cb->lh_next = NULL; 160 if (last != NULL) { 161 last->lh_next = cb; 162 } else { /* List currently empty */ 163 lsx_handler = cb; 164 } 165 registered = TRUE; 166 } else { 167 rv = BCM_E_MEMORY; 168 } 169 } 170 171 LSX_UNLOCK; 172 173 if (registered) { /* Make sure registered with lower layer */ 174 _bcmx_linkscan_rlink_register(); 175 } 176 177 return rv; 178 } 179 180 181 int 182 bcmx_linkscan_unregister(bcmx_link_notify_f ln_cb) 183 { 184 lsx_handler_t *cb, *prev = NULL; 185 186 LSX_CHECK_INIT; 187 188 LOG_VERBOSE(BSL_LS_BCMX_LINK, 189 (BSL_META("BCMX LS unreg, %p\n"), 190 ln_cb)); 191 192 LSX_LOCK; 193 cb = lsx_handler; 194 while (cb != NULL) { 195 if (cb->lh_f == ln_cb) { 196 if (prev != NULL) { 197 prev->lh_next = cb->lh_next; 198 } else { 199 lsx_handler = cb->lh_next; 200 } 201 sal_free(cb); 202 break; 203 } 204 prev = cb; 205 cb = cb->lh_next; 206 } 207 LSX_UNLOCK; 208 209 return BCM_E_NONE; 210 } 211 212 213 /* 214 * Function: 215 * bcmx_linkscan_enable_set 216 * Purpose: 217 * Set linkscan state 218 * Parameters: 219 * us - Interval in microseconds; see notes 220 * Returns: 221 * BCM_E_XXX 222 * Notes: 223 * If us == 0, disables linkscan at lowest layer. Note that 224 * registrations may still exist in this case. 225 * 226 * If us < 0, then a sync is performed by looking for a unit 227 * which returns success; this value is then used to set all 228 * other devices. 229 * 230 * If no device found indicating current setting, _E_FAIL 231 * is returned. 232 */ 233 234 int 235 bcmx_linkscan_enable_set(int us) 236 { 237 int rv = BCM_E_UNAVAIL, tmp_rv; 238 int found = FALSE; 239 int interval; 240 int bcm_unit, i; 241 242 LSX_CHECK_INIT; 243 244 LOG_VERBOSE(BSL_LS_BCMX_LINK, 245 (BSL_META("BCMX LS enable set, %d\n"), 246 us)); 247 248 if (us < 0) { /* Sync: See notes above */ 249 250 /* Try to get current enable setting */ 251 BCMX_UNIT_ITER(bcm_unit, i) { 252 tmp_rv = bcm_linkscan_enable_get(bcm_unit, &interval); 253 if (BCMX_LINK_GET_IS_VALID(bcm_unit, tmp_rv)) { 254 rv = tmp_rv; 255 if (BCM_SUCCESS(tmp_rv)) { 256 found = TRUE; 257 } 258 break; 259 } 260 } 261 262 if (found) { /* Sync everyone to this setting */ 263 lsx_interval = interval; 264 BCMX_UNIT_ITER(bcm_unit, i) { 265 tmp_rv = bcm_linkscan_register(bcm_unit, bcmx_bcm_ls_cb); 266 BCM_IF_ERROR_RETURN 267 (BCMX_LINK_SET_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 268 } 269 } else { 270 return BCM_E_FAIL; /* Could not synchronize */ 271 } 272 273 } else { /* Just set on all current units */ 274 275 if (us > 0) { /* Enabling: Ensure registered */ 276 (void)_bcmx_linkscan_rlink_register(); 277 } 278 lsx_interval = us; 279 BCMX_UNIT_ITER(bcm_unit, i) { 280 tmp_rv = bcm_linkscan_enable_set(bcm_unit, us); 281 BCM_IF_ERROR_RETURN(BCMX_LINK_SET_ERROR_CHECK(bcm_unit, tmp_rv, &rv)); 282 } 283 } 284 285 return rv; 286 } 287 288 289 /* 290 * Function: 291 * bcmx_linkscan_enable_get 292 * Purpose: 293 * Get linkscan state 294 * Parameters: 295 * us - (OUT) Current interval in microseconds 296 * consistent - (OUT) If non-NULL, check for consistency 297 * on all BCMX units and return Boolean value here. 298 * Returns: 299 * BCM_E_XXX 300 */ 301 302 int 303 bcmx_linkscan_enable_get(int *us, int *consistent) 304 { 305 int bcm_unit, i; 306 int rv = BCM_E_UNAVAIL, tmp_rv; 307 int tmp_us; 308 309 LSX_CHECK_INIT; 310 311 312 if (lsx_interval >= 0) { /* Has been initialized */ 313 *us = lsx_interval; 314 } else { /* Look for a unit with info */ 315 BCMX_UNIT_ITER(bcm_unit, i) { 316 tmp_rv = bcm_linkscan_enable_get(bcm_unit, &tmp_us); 317 if (BCMX_LINK_GET_IS_VALID(bcm_unit, tmp_rv)) { 318 rv = tmp_rv; 319 if (BCM_SUCCESS(tmp_rv)) { 320 *us = lsx_interval = tmp_us; 321 } 322 break; 323 } 324 } 325 } 326 327 if (lsx_interval < 0) { /* Still unitialized */ 328 return rv; 329 } 330 331 if (consistent != NULL) { /* Check for consistency across units */ 332 *consistent = TRUE; 333 334 BCMX_UNIT_ITER(bcm_unit, i) { 335 tmp_rv = bcm_linkscan_enable_get(bcm_unit, &tmp_us); 336 if (BCMX_LINK_GET_IS_VALID(bcm_unit, tmp_rv)) { 337 rv = tmp_rv; 338 if (BCM_SUCCESS(tmp_rv)) { 339 if (tmp_us != lsx_interval) { 340 *consistent = FALSE; 341 break; 342 } 343 } else { 344 break; 345 } 346 } 347 } 348 } 349 350 return rv; 351 } 352 353 354 /* 355 * Function: 356 * bcmx_linkscan_enable_port_get 357 * Purpose: 358 * Determine whether or not linkscan is managing a given port 359 * Parameters: 360 * lport - Logical port to check. 361 * Returns: 362 * BCM_E_NONE - port being scanned 363 * BCM_E_PARAM - invalid logical port 364 * BCM_E_DISABLED - port not being scanned 365 */ 366 367 int 368 bcmx_linkscan_enable_port_get(bcmx_lport_t lport) 369 { 370 int bcm_unit; 371 bcm_port_t bcm_port; 372 373 LSX_CHECK_INIT; 374 375 if (BCM_FAILURE 376 (_bcmx_dest_to_unit_port(lport, &bcm_unit, &bcm_port, 377 BCMX_DEST_CONVERT_DEFAULT))) { 378 return BCM_E_PARAM; 379 } 380 381 return bcm_linkscan_enable_port_get(bcm_unit, bcm_port); 382 } 383 384 /* 385 * Function: 386 * bcmx_linkscan_device_add 387 * Purpose: 388 * Add a unit to linkscanning 389 * Notes: 390 * Registers linkscan callback 391 */ 392 393 int 394 bcmx_linkscan_device_add(int bcm_unit) 395 { 396 int rv; 397 398 LSX_CHECK_INIT; 399 400 if (lsx_interval >= 0) { /* Has been initialized */ 401 rv = bcm_linkscan_enable_set(bcm_unit, lsx_interval); 402 if (rv < 0) { 403 LOG_WARN(BSL_LS_BCMX_COMMON, 404 (BSL_META("WARN: BCMX LS dev add enable, %d: %s\n"), 405 rv, bcm_errmsg(rv))); 406 } 407 } 408 409 /* Always register if possible */ 410 rv = bcm_linkscan_register(bcm_unit, bcmx_bcm_ls_cb); 411 412 if (rv < 0) { 413 LOG_WARN(BSL_LS_BCMX_COMMON, 414 (BSL_META("WARN: BCMX LS device add reg, %d: %s\n"), 415 rv, bcm_errmsg(rv))); 416 } 417 418 return rv; 419 } 420 421 /* 422 * Function: 423 * bcmx_linkscan_device_remove 424 * Purpose: 425 * Remove a unit from linkscanning 426 */ 427 428 int 429 bcmx_linkscan_device_remove(int bcm_unit) 430 { 431 LSX_CHECK_INIT; 432 433 return bcm_linkscan_unregister(bcm_unit, bcmx_bcm_ls_cb); 434 } 435 436 437 438 #if defined(BROADCOM_DEBUG) 439 void 440 bcmx_linkscan_dump(void) 441 { 442 lsx_handler_t *ent; 443 444 if (lsx_lock == NULL) { 445 LOG_INFO(BSL_LS_BCMX_COMMON, 446 (BSL_META("BCMX linkscan not initialized\n"))); 447 return; 448 } 449 450 LOG_INFO(BSL_LS_BCMX_COMMON, 451 (BSL_META("BCMX linkscan interval %d%s\n"), 452 lsx_interval, 453 lsx_interval < 0 ? " (un-init)" : "")); 454 455 LOG_INFO(BSL_LS_BCMX_COMMON, 456 (BSL_META("LSX handler list: \n"))); 457 for (ent = lsx_handler; ent != NULL; ent = ent->lh_next) { 458 LOG_INFO(BSL_LS_BCMX_COMMON, 459 (BSL_META(" Fn %p.\n"), 460 ent->lh_f)); 461 } 462 463 } 464 465 void 466 bcmx_rlink_dump(void) 467 { 468 #ifdef BCM_RPC_SUPPORT 469 extern void bcm_rlink_dump(void); 470 471 bcm_rlink_dump(); 472 #endif /* BCM_RPC_SUPPORT */ 473 bcmx_linkscan_dump(); 474 } 475 #endif /* BROADCOM_DEBUG */