1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2012-2021 Chelsio Communications, Inc. 5 * All rights reserved. 6 * Written by: Navdeep Parhar <np@FreeBSD.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include "opt_inet.h" 34 #include "opt_inet6.h" 35 36 #include <sys/types.h> 37 #include <sys/ck.h> 38 #include <sys/eventhandler.h> 39 #include <sys/malloc.h> 40 #include <sys/rmlock.h> 41 #include <sys/sbuf.h> 42 #include <sys/socket.h> 43 #include <sys/taskqueue.h> 44 #include <net/if.h> 45 #include <net/if_var.h> 46 #include <netinet/in.h> 47 #include <netinet6/in6_var.h> 48 #include <netinet6/scope6_var.h> 49 50 #include "common/common.h" 51 #include "t4_clip.h" 52 53 /* 54 * Code to deal with the Compressed Local IPv6 (CLIP) table in the ASIC. 55 * 56 * The driver maintains a global CLIP database (clip_db) of IPv6 addresses and a 57 * per-adapter CLIP table (sc->clip_table) with entries that point to an IPv6 in 58 * the clip_db. All access is protected by a single global lock (clip_db_lock). 59 * The correct lock order is clip lock before synchronized op. 60 * 61 * By default (hw.cxgbe.clip_db_auto=1) all local IPv6 addresses are added to 62 * the db. Addresses are also added on-demand when the driver allocates an 63 * entry for a filter, TOE tid, etc. krn_ref counts the number of times an 64 * address appears in the system. adp_ref counts the number of adapters that 65 * have that address in their CLIP table. If both are 0 then the entry is 66 * evicted from the db. Consumers of the CLIP table entry (filters, TOE tids) 67 * are tracked in ce->refcount. Driver ioctls let external consumers add/remove 68 * addresses from the CLIP table. 69 */ 70 71 #if defined(INET6) 72 struct clip_db_entry { 73 LIST_ENTRY(clip_db_entry) link; /* clip_db hash linkage */ 74 struct in6_addr lip; 75 u_int krn_ref; /* # of times this IP6 appears in list of all IP6 */ 76 u_int adp_ref; /* # of adapters with this IP6 in their CLIP */ 77 u_int tmp_ref; /* Used only during refresh */ 78 }; 79 80 struct clip_entry { 81 LIST_ENTRY(clip_entry) link; /* clip_table hash linkage */ 82 TAILQ_ENTRY(clip_entry) plink; /* clip_pending linkage */ 83 struct clip_db_entry *cde; 84 int16_t clip_idx; /* index in the hw table */ 85 bool pending; /* in clip_pending list */ 86 int refcount; 87 }; 88 89 static eventhandler_tag ifaddr_evhandler; 90 static struct mtx clip_db_lock; 91 static LIST_HEAD(, clip_db_entry) *clip_db; 92 static u_long clip_db_mask; 93 static int clip_db_gen; 94 static struct task clip_db_task; 95 96 static int add_lip(struct adapter *, struct in6_addr *, int16_t *); 97 static int del_lip(struct adapter *, struct in6_addr *); 98 static void t4_clip_db_task(void *, int); 99 static void t4_clip_task(void *, int); 100 static void update_clip_db(void); 101 static int update_sw_clip_table(struct adapter *); 102 static int update_hw_clip_table(struct adapter *); 103 static void update_clip_table(struct adapter *, void *); 104 static int sysctl_clip_db(SYSCTL_HANDLER_ARGS); 105 static int sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS); 106 static struct clip_db_entry *lookup_clip_db_entry(struct in6_addr *, bool); 107 static struct clip_entry *lookup_clip_entry(struct adapter *, struct in6_addr *, 108 bool); 109 110 SYSCTL_PROC(_hw_cxgbe, OID_AUTO, clip_db, CTLTYPE_STRING | CTLFLAG_RD | 111 CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL, 0, sysctl_clip_db, "A", 112 "CLIP database"); 113 114 int t4_clip_db_auto = 1; 115 SYSCTL_PROC(_hw_cxgbe, OID_AUTO, clip_db_auto, CTLTYPE_INT | CTLFLAG_RWTUN | 116 CTLFLAG_MPSAFE, NULL, 0, sysctl_clip_db_auto, "I", 117 "Add local IPs to CLIP db automatically (0 = no, 1 = yes)"); 118 119 static inline uint32_t 120 clip_hashfn(struct in6_addr *addr) 121 { 122 return (fnv_32_buf(addr, sizeof(*addr), FNV1_32_INIT) & clip_db_mask); 123 } 124 125 static inline struct clip_db_entry * 126 alloc_clip_db_entry(struct in6_addr *in6) 127 { 128 struct clip_db_entry *cde; 129 130 cde = malloc(sizeof(*cde), M_CXGBE, M_NOWAIT | M_ZERO); 131 if (__predict_true(cde != NULL)) 132 memcpy(&cde->lip, in6, sizeof(cde->lip)); 133 134 return (cde); 135 } 136 137 static inline struct clip_entry * 138 alloc_clip_entry(struct clip_db_entry *cde) 139 { 140 struct clip_entry *ce; 141 142 mtx_assert(&clip_db_lock, MA_OWNED); 143 144 ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT | M_ZERO); 145 if (__predict_true(ce != NULL)) { 146 ce->cde = cde; 147 cde->adp_ref++; 148 ce->clip_idx = -1; 149 } 150 151 return (ce); 152 } 153 154 /* 155 * Look up the IP6 address in the CLIP db. If add is set then an entry for the 156 * IP6 will be added to the db. 157 */ 158 static struct clip_db_entry * 159 lookup_clip_db_entry(struct in6_addr *in6, bool add) 160 { 161 struct clip_db_entry *cde; 162 const int bucket = clip_hashfn(in6); 163 164 mtx_assert(&clip_db_lock, MA_OWNED); 165 166 LIST_FOREACH(cde, &clip_db[bucket], link) { 167 if (IN6_ARE_ADDR_EQUAL(&cde->lip, in6)) 168 return (cde); 169 } 170 171 /* Not found. Create a new entry if requested. */ 172 if (add) { 173 cde = alloc_clip_db_entry(in6); 174 if (cde != NULL) 175 LIST_INSERT_HEAD(&clip_db[bucket], cde, link); 176 } 177 178 return (cde); 179 } 180 181 /* 182 * Look up the IP6 address in the CLIP db. If add is set then an entry for the 183 * IP6 will be added to the db. 184 */ 185 static struct clip_entry * 186 lookup_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add) 187 { 188 struct clip_db_entry *cde; 189 struct clip_entry *ce; 190 const int bucket = clip_hashfn(in6); 191 192 mtx_assert(&clip_db_lock, MA_OWNED); 193 194 cde = lookup_clip_db_entry(in6, add); 195 if (cde == NULL) 196 return (NULL); 197 198 LIST_FOREACH(ce, &sc->clip_table[bucket], link) { 199 if (ce->cde == cde) 200 return (ce); 201 } 202 203 /* Not found. Create a new entry if requested. */ 204 if (add) { 205 ce = alloc_clip_entry(cde); 206 if (ce != NULL) { 207 LIST_INSERT_HEAD(&sc->clip_table[bucket], ce, link); 208 TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink); 209 ce->pending = true; 210 } 211 } 212 213 return (ce); 214 } 215 216 static int 217 add_lip(struct adapter *sc, struct in6_addr *lip, int16_t *idx) 218 { 219 struct fw_clip_cmd c; 220 int rc; 221 222 ASSERT_SYNCHRONIZED_OP(sc); 223 224 memset(&c, 0, sizeof(c)); 225 c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST | 226 F_FW_CMD_WRITE); 227 c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c)); 228 c.ip_hi = *(uint64_t *)&lip->s6_addr[0]; 229 c.ip_lo = *(uint64_t *)&lip->s6_addr[8]; 230 231 rc = -t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c); 232 if (rc == 0 && idx != NULL) 233 *idx = G_FW_CLIP_CMD_INDEX(ntohl(c.alloc_to_len16)); 234 return (rc); 235 } 236 237 static int 238 del_lip(struct adapter *sc, struct in6_addr *lip) 239 { 240 struct fw_clip_cmd c; 241 242 ASSERT_SYNCHRONIZED_OP(sc); 243 244 memset(&c, 0, sizeof(c)); 245 c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST | 246 F_FW_CMD_READ); 247 c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c)); 248 c.ip_hi = *(uint64_t *)&lip->s6_addr[0]; 249 c.ip_lo = *(uint64_t *)&lip->s6_addr[8]; 250 251 return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c)); 252 } 253 #endif 254 255 struct clip_entry * 256 t4_get_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add) 257 { 258 #ifdef INET6 259 struct clip_entry *ce; 260 bool schedule = false; 261 262 mtx_lock(&clip_db_lock); 263 ce = lookup_clip_entry(sc, in6, add); 264 if (ce != NULL) { 265 MPASS(ce->cde->adp_ref > 0); 266 if (++ce->refcount == 1 && ce->pending && ce->clip_idx != -1) { 267 /* 268 * Valid entry that was waiting to be deleted. It is in 269 * use now so take it off the pending list. 270 */ 271 TAILQ_REMOVE(&sc->clip_pending, ce, plink); 272 ce->pending = false; 273 } 274 if (ce->clip_idx == -1 && update_hw_clip_table(sc) != 0) 275 schedule = true; 276 } 277 mtx_unlock(&clip_db_lock); 278 if (schedule) 279 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0); 280 281 return (ce); 282 #else 283 return (NULL); 284 #endif 285 } 286 287 void 288 t4_hold_clip_entry(struct adapter *sc, struct clip_entry *ce) 289 { 290 #ifdef INET6 291 MPASS(ce != NULL); 292 MPASS(ce->cde->adp_ref > 0); 293 294 mtx_lock(&clip_db_lock); 295 MPASS(ce->refcount > 0); /* Caller should already have a reference */ 296 ce->refcount++; 297 mtx_unlock(&clip_db_lock); 298 #endif 299 } 300 301 #ifdef INET6 302 static void 303 release_clip_entry_locked(struct adapter *sc, struct clip_entry *ce) 304 { 305 struct clip_db_entry *cde; 306 307 mtx_assert(&clip_db_lock, MA_OWNED); 308 MPASS(ce->refcount > 0); 309 cde = ce->cde; 310 MPASS(cde->adp_ref > 0); 311 if (--ce->refcount == 0 && cde->krn_ref == 0) { 312 if (ce->clip_idx == -1) { 313 /* Was never written to the hardware. */ 314 MPASS(ce->pending); 315 TAILQ_REMOVE(&sc->clip_pending, ce, plink); 316 LIST_REMOVE(ce, link); 317 free(ce, M_CXGBE); 318 if (--cde->adp_ref == 0) { 319 LIST_REMOVE(cde, link); 320 free(cde, M_CXGBE); 321 } 322 } else { 323 /* 324 * Valid entry is now unused, add to the pending list 325 * for deletion. Its refcount was 1 on entry so it 326 * can't already be pending. 327 */ 328 MPASS(!ce->pending); 329 TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink); 330 ce->pending = true; 331 } 332 } 333 } 334 #endif 335 336 void 337 t4_release_clip_entry(struct adapter *sc, struct clip_entry *ce) 338 { 339 #ifdef INET6 340 MPASS(ce != NULL); 341 342 mtx_lock(&clip_db_lock); 343 release_clip_entry_locked(sc, ce); 344 /* 345 * This isn't a manual release via the ioctl. No need to update the 346 * hw right now even if the release resulted in the entry being queued 347 * for deletion. 348 */ 349 mtx_unlock(&clip_db_lock); 350 #endif 351 } 352 353 int 354 t4_release_clip_addr(struct adapter *sc, struct in6_addr *in6) 355 { 356 int rc = ENOTSUP; 357 #ifdef INET6 358 struct clip_entry *ce; 359 bool schedule = false; 360 361 mtx_lock(&clip_db_lock); 362 ce = lookup_clip_entry(sc, in6, false); 363 if (ce == NULL) 364 rc = ENOENT; 365 else if (ce->refcount == 0) 366 rc = EIO; 367 else { 368 release_clip_entry_locked(sc, ce); 369 if (update_hw_clip_table(sc) != 0) 370 schedule = true; 371 rc = 0; 372 } 373 mtx_unlock(&clip_db_lock); 374 if (schedule) 375 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0); 376 #endif 377 return (rc); 378 } 379 380 #ifdef INET6 381 void 382 t4_init_clip_table(struct adapter *sc) 383 { 384 TAILQ_INIT(&sc->clip_pending); 385 TIMEOUT_TASK_INIT(taskqueue_thread, &sc->clip_task, 0, t4_clip_task, sc); 386 sc->clip_gen = -1; 387 sc->clip_table = hashinit(CLIP_HASH_SIZE, M_CXGBE, &sc->clip_mask); 388 389 /* Both the hashes must use the same bucket for the same key. */ 390 if (sc->clip_table != NULL) 391 MPASS(sc->clip_mask == clip_db_mask); 392 /* 393 * Don't bother forcing an update of the clip table when the 394 * adapter is initialized. Before an interface can be used it 395 * must be assigned an address which will trigger the event 396 * handler to update the table. 397 */ 398 } 399 400 /* 401 * Returns true if any additions or deletions were made to the CLIP DB. 402 */ 403 static void 404 update_clip_db(void) 405 { 406 VNET_ITERATOR_DECL(vnet_iter); 407 struct rm_priotracker in6_ifa_tracker; 408 struct in6_addr *in6, tin6; 409 struct in6_ifaddr *ia; 410 struct clip_db_entry *cde, *cde_tmp; 411 int i, addel; 412 413 VNET_LIST_RLOCK(); 414 IN6_IFADDR_RLOCK(&in6_ifa_tracker); 415 mtx_lock(&clip_db_lock); 416 VNET_FOREACH(vnet_iter) { 417 CURVNET_SET_QUIET(vnet_iter); 418 CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { 419 if (if_getflags(ia->ia_ifp) & IFF_LOOPBACK) 420 continue; 421 in6 = &ia->ia_addr.sin6_addr; 422 KASSERT(!IN6_IS_ADDR_MULTICAST(in6), 423 ("%s: mcast address in in6_ifaddr list", __func__)); 424 if (IN6_IS_ADDR_LOOPBACK(in6)) 425 continue; 426 427 if (IN6_IS_SCOPE_EMBED(in6)) { 428 tin6 = *in6; 429 in6 = &tin6; 430 in6_clearscope(in6); 431 } 432 cde = lookup_clip_db_entry(in6, true); 433 if (cde == NULL) 434 continue; 435 cde->tmp_ref++; 436 } 437 CURVNET_RESTORE(); 438 } 439 440 addel = 0; 441 for (i = 0; i <= clip_db_mask; i++) { 442 LIST_FOREACH_SAFE(cde, &clip_db[i], link, cde_tmp) { 443 if (cde->krn_ref == 0 && cde->tmp_ref > 0) { 444 addel++; /* IP6 addr added. */ 445 } else if (cde->krn_ref > 0 && cde->tmp_ref == 0) { 446 if (cde->adp_ref == 0) { 447 LIST_REMOVE(cde, link); 448 free(cde, M_CXGBE); 449 continue; 450 } 451 addel++; /* IP6 addr deleted. */ 452 } 453 cde->krn_ref = cde->tmp_ref; 454 cde->tmp_ref = 0; 455 } 456 } 457 if (addel > 0) 458 clip_db_gen++; 459 mtx_unlock(&clip_db_lock); 460 IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); 461 VNET_LIST_RUNLOCK(); 462 463 } 464 465 /* 466 * Update the CLIP db and then update the CLIP tables on all the adapters. 467 */ 468 static void 469 t4_clip_db_task(void *arg, int count) 470 { 471 update_clip_db(); 472 t4_iterate(update_clip_table, NULL); 473 } 474 475 /* 476 * Refresh the sw CLIP table for this adapter from the global CLIP db. Entries 477 * that need to be added or deleted from the hardware CLIP table are placed on a 478 * pending list but the hardware is not touched. The pending list is something 479 * reasonable even if this fails so it's ok to apply that to the hardware. 480 */ 481 static int 482 update_sw_clip_table(struct adapter *sc) 483 { 484 struct clip_db_entry *cde; 485 struct clip_entry *ce, *ce_temp; 486 int i; 487 bool found; 488 489 mtx_assert(&clip_db_lock, MA_OWNED); 490 491 /* 492 * We are about to rebuild the pending list from scratch. Deletions are 493 * placed before additions because that's how we want to submit them to 494 * the hardware. 495 */ 496 TAILQ_INIT(&sc->clip_pending); 497 498 /* 499 * Walk the sw CLIP table first. We want to reset every entry's pending 500 * status as we're rebuilding the pending list. 501 */ 502 for (i = 0; i <= clip_db_mask; i++) { 503 LIST_FOREACH_SAFE(ce, &sc->clip_table[i], link, ce_temp) { 504 cde = ce->cde; 505 MPASS(cde->adp_ref > 0); 506 if (ce->refcount != 0 || cde->krn_ref != 0) { 507 /* 508 * Entry should stay in the CLIP. 509 */ 510 511 if (ce->clip_idx != -1) { 512 ce->pending = false; 513 } else { 514 /* Was never added, carry forward. */ 515 MPASS(ce->pending); 516 TAILQ_INSERT_TAIL(&sc->clip_pending, ce, 517 plink); 518 } 519 continue; 520 } 521 522 /* 523 * Entry should be removed from the CLIP. 524 */ 525 526 if (ce->clip_idx != -1) { 527 ce->pending = true; 528 TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink); 529 } else { 530 /* Was never added, free right now. */ 531 MPASS(ce->pending); 532 LIST_REMOVE(ce, link); 533 free(ce, M_CXGBE); 534 if (--cde->adp_ref == 0) { 535 LIST_REMOVE(cde, link); 536 free(cde, M_CXGBE); 537 } 538 } 539 } 540 } 541 542 for (i = 0; i <= clip_db_mask; i++) { 543 LIST_FOREACH(cde, &clip_db[i], link) { 544 if (cde->krn_ref == 0) 545 continue; 546 547 found = false; 548 LIST_FOREACH(ce, &sc->clip_table[i], link) { 549 if (ce->cde == cde) { 550 found = true; 551 break; 552 } 553 } 554 if (found) 555 continue; 556 ce = alloc_clip_entry(cde); 557 if (ce == NULL) 558 return (ENOMEM); 559 LIST_INSERT_HEAD(&sc->clip_table[i], ce, link); 560 TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink); 561 ce->pending = true; 562 } 563 } 564 565 sc->clip_gen = clip_db_gen; 566 return (0); 567 } 568 569 static int 570 update_hw_clip_table(struct adapter *sc) 571 { 572 struct clip_db_entry *cde; 573 struct clip_entry *ce; 574 int rc; 575 char ip[INET6_ADDRSTRLEN]; 576 577 mtx_assert(&clip_db_lock, MA_OWNED); 578 rc = begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip"); 579 if (rc != 0) 580 return (rc); 581 if (hw_off_limits(sc)) 582 goto done; /* with rc = 0, we don't want to reschedule. */ 583 while (!TAILQ_EMPTY(&sc->clip_pending)) { 584 ce = TAILQ_FIRST(&sc->clip_pending); 585 MPASS(ce->pending); 586 cde = ce->cde; 587 MPASS(cde->adp_ref > 0); 588 589 if (ce->clip_idx == -1) { 590 /* 591 * Entry was queued for addition to the HW CLIP. 592 */ 593 594 if (ce->refcount == 0 && cde->krn_ref == 0) { 595 /* No need to add to HW CLIP. */ 596 TAILQ_REMOVE(&sc->clip_pending, ce, plink); 597 LIST_REMOVE(ce, link); 598 free(ce, M_CXGBE); 599 if (--cde->adp_ref == 0) { 600 LIST_REMOVE(cde, link); 601 free(cde, M_CXGBE); 602 } 603 } else { 604 /* Add to the HW CLIP. */ 605 rc = add_lip(sc, &cde->lip, &ce->clip_idx); 606 if (rc == FW_ENOMEM) { 607 /* CLIP full, no point in retrying. */ 608 rc = 0; 609 goto done; 610 } 611 if (rc != 0) { 612 inet_ntop(AF_INET6, &cde->lip, &ip[0], 613 sizeof(ip)); 614 CH_ERR(sc, "add_lip(%s) failed: %d\n", 615 ip, rc); 616 goto done; 617 } 618 MPASS(ce->clip_idx != -1); 619 TAILQ_REMOVE(&sc->clip_pending, ce, plink); 620 ce->pending = false; 621 } 622 } else { 623 /* 624 * Entry was queued for deletion from the HW CLIP. 625 */ 626 627 if (ce->refcount == 0 && cde->krn_ref == 0) { 628 /* 629 * Delete from the HW CLIP. Delete should never 630 * fail so we always log an error. But if the 631 * failure is that the entry wasn't found in the 632 * CLIP then we carry on as if it was deleted. 633 */ 634 rc = del_lip(sc, &cde->lip); 635 if (rc != 0) 636 CH_ERR(sc, "del_lip(%s) failed: %d\n", 637 ip, rc); 638 if (rc == FW_EPROTO) 639 rc = 0; 640 if (rc != 0) 641 goto done; 642 643 TAILQ_REMOVE(&sc->clip_pending, ce, plink); 644 LIST_REMOVE(ce, link); 645 free(ce, M_CXGBE); 646 if (--cde->adp_ref == 0) { 647 LIST_REMOVE(cde, link); 648 free(cde, M_CXGBE); 649 } 650 } else { 651 /* No need to delete from HW CLIP. */ 652 TAILQ_REMOVE(&sc->clip_pending, ce, plink); 653 ce->pending = false; 654 } 655 } 656 } 657 done: 658 end_synchronized_op(sc, LOCK_HELD); 659 return (rc); 660 } 661 662 static void 663 update_clip_table(struct adapter *sc, void *arg __unused) 664 { 665 bool reschedule; 666 667 if (sc->clip_table == NULL) 668 return; 669 670 reschedule = false; 671 mtx_lock(&clip_db_lock); 672 if (sc->clip_gen != clip_db_gen && update_sw_clip_table(sc) != 0) 673 reschedule = true; 674 if (!TAILQ_EMPTY(&sc->clip_pending) && update_hw_clip_table(sc) != 0) 675 reschedule = true; 676 mtx_unlock(&clip_db_lock); 677 if (reschedule) 678 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 679 -hz / 4); 680 } 681 682 /* 683 * Update the CLIP table of the specified adapter. 684 */ 685 static void 686 t4_clip_task(void *sc, int count) 687 { 688 update_clip_table(sc, NULL); 689 } 690 691 void 692 t4_destroy_clip_table(struct adapter *sc) 693 { 694 struct clip_entry *ce, *ce_temp; 695 int i; 696 697 mtx_lock(&clip_db_lock); 698 if (sc->clip_table == NULL) 699 goto done; /* CLIP was never initialized. */ 700 for (i = 0; i <= sc->clip_mask; i++) { 701 LIST_FOREACH_SAFE(ce, &sc->clip_table[i], link, ce_temp) { 702 MPASS(ce->refcount == 0); 703 MPASS(ce->cde->adp_ref > 0); 704 #if 0 705 del_lip(sc, &ce->lip); 706 #endif 707 LIST_REMOVE(ce, link); 708 if (--ce->cde->adp_ref == 0 && ce->cde->krn_ref == 0) { 709 LIST_REMOVE(ce->cde, link); 710 free(ce->cde, M_CXGBE); 711 } 712 free(ce, M_CXGBE); 713 } 714 } 715 hashdestroy(sc->clip_table, M_CXGBE, sc->clip_mask); 716 sc->clip_table = NULL; 717 done: 718 mtx_unlock(&clip_db_lock); 719 } 720 721 static void 722 t4_ifaddr_event(void *arg __unused, if_t ifp, struct ifaddr *ifa, 723 int event) 724 { 725 struct in6_addr *in6; 726 727 if (t4_clip_db_auto == 0) 728 return; /* Automatic updates not allowed. */ 729 if (ifa->ifa_addr->sa_family != AF_INET6) 730 return; 731 if (if_getflags(ifp) & IFF_LOOPBACK) 732 return; 733 in6 = &((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr; 734 if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_MULTICAST(in6)) 735 return; 736 737 taskqueue_enqueue(taskqueue_thread, &clip_db_task); 738 } 739 740 int 741 sysctl_clip(SYSCTL_HANDLER_ARGS) 742 { 743 struct adapter *sc = arg1; 744 struct clip_entry *ce; 745 struct sbuf *sb; 746 int i, rc, header = 0; 747 char ip[INET6_ADDRSTRLEN]; 748 749 rc = sysctl_wire_old_buffer(req, 0); 750 if (rc != 0) 751 return (rc); 752 753 sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req); 754 if (sb == NULL) 755 return (ENOMEM); 756 757 mtx_lock(&clip_db_lock); 758 for (i = 0; i <= sc->clip_mask; i++) { 759 LIST_FOREACH(ce, &sc->clip_table[i], link) { 760 if (header == 0) { 761 sbuf_printf(sb, "%-4s %-4s %s", "Indx", "Refs", 762 "IP address"); 763 header = 1; 764 } 765 inet_ntop(AF_INET6, &ce->cde->lip, &ip[0], sizeof(ip)); 766 if (ce->clip_idx == -1) { 767 sbuf_printf(sb, "\n%-4s %-4d %s", "-", 768 ce->refcount, ip); 769 } else { 770 sbuf_printf(sb, "\n%-4d %-4d %s", ce->clip_idx, 771 ce->refcount, ip); 772 } 773 } 774 } 775 mtx_unlock(&clip_db_lock); 776 777 rc = sbuf_finish(sb); 778 sbuf_delete(sb); 779 780 return (rc); 781 } 782 783 static int 784 sysctl_clip_db(SYSCTL_HANDLER_ARGS) 785 { 786 struct clip_db_entry *cde; 787 struct sbuf *sb; 788 int i, rc, header = 0; 789 char ip[INET6_ADDRSTRLEN]; 790 791 rc = sysctl_wire_old_buffer(req, 0); 792 if (rc != 0) 793 return (rc); 794 795 sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req); 796 if (sb == NULL) 797 return (ENOMEM); 798 799 mtx_lock(&clip_db_lock); 800 for (i = 0; i <= clip_db_mask; i++) { 801 LIST_FOREACH(cde, &clip_db[i], link) { 802 MPASS(cde->tmp_ref == 0); 803 if (header == 0) { 804 sbuf_printf(sb, "%-4s %-4s %s", "Kref", "Aref", 805 "IP address"); 806 header = 1; 807 } 808 inet_ntop(AF_INET6, &cde->lip, &ip[0], sizeof(ip)); 809 sbuf_printf(sb, "\n%-4d %-4d %s", cde->krn_ref, 810 cde->adp_ref, ip); 811 } 812 } 813 mtx_unlock(&clip_db_lock); 814 815 rc = sbuf_finish(sb); 816 sbuf_delete(sb); 817 818 return (rc); 819 } 820 821 static int 822 sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS) 823 { 824 int rc, val; 825 826 val = t4_clip_db_auto; 827 rc = sysctl_handle_int(oidp, &val, 0, req); 828 if (rc != 0 || req->newptr == NULL) 829 return (rc); 830 831 if (val == 0 || val == 1) 832 t4_clip_db_auto = val; 833 else { 834 /* 835 * Writing a value other than 0 or 1 forces a one-time update of 836 * the clip_db directly in the sysctl and not in some taskqueue. 837 */ 838 t4_clip_db_task(NULL, 0); 839 } 840 841 return (0); 842 } 843 844 void 845 t4_clip_modload(void) 846 { 847 mtx_init(&clip_db_lock, "clip_db", NULL, MTX_DEF); 848 clip_db = hashinit(CLIP_HASH_SIZE, M_CXGBE, &clip_db_mask); 849 TASK_INIT(&clip_db_task, 0, t4_clip_db_task, NULL); 850 ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event_ext, 851 t4_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY); 852 } 853 854 void 855 t4_clip_modunload(void) 856 { 857 struct clip_db_entry *cde; 858 int i; 859 860 EVENTHANDLER_DEREGISTER(ifaddr_event_ext, ifaddr_evhandler); 861 taskqueue_drain(taskqueue_thread, &clip_db_task); 862 mtx_lock(&clip_db_lock); 863 for (i = 0; i <= clip_db_mask; i++) { 864 while ((cde = LIST_FIRST(&clip_db[i])) != NULL) { 865 MPASS(cde->tmp_ref == 0); 866 MPASS(cde->adp_ref == 0); 867 LIST_REMOVE(cde, link); 868 free(cde, M_CXGBE); 869 } 870 } 871 mtx_unlock(&clip_db_lock); 872 hashdestroy(clip_db, M_CXGBE, clip_db_mask); 873 mtx_destroy(&clip_db_lock); 874 } 875 #endif 876