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