xref: /freebsd/sys/netinet/tcp_log_buf.c (revision 57cc27a3325af74553e852851d5e84e308ae79f4)
12529f56eSJonathan T. Looney /*-
22529f56eSJonathan T. Looney  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
32529f56eSJonathan T. Looney  *
452467047SWarner Losh  * Copyright (c) 2016-2018 Netflix, Inc.
52529f56eSJonathan T. Looney  *
62529f56eSJonathan T. Looney  * Redistribution and use in source and binary forms, with or without
72529f56eSJonathan T. Looney  * modification, are permitted provided that the following conditions
82529f56eSJonathan T. Looney  * are met:
92529f56eSJonathan T. Looney  * 1. Redistributions of source code must retain the above copyright
102529f56eSJonathan T. Looney  *    notice, this list of conditions and the following disclaimer.
112529f56eSJonathan T. Looney  * 2. Redistributions in binary form must reproduce the above copyright
122529f56eSJonathan T. Looney  *    notice, this list of conditions and the following disclaimer in the
132529f56eSJonathan T. Looney  *    documentation and/or other materials provided with the distribution.
142529f56eSJonathan T. Looney  *
152529f56eSJonathan T. Looney  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162529f56eSJonathan T. Looney  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172529f56eSJonathan T. Looney  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182529f56eSJonathan T. Looney  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192529f56eSJonathan T. Looney  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202529f56eSJonathan T. Looney  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212529f56eSJonathan T. Looney  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222529f56eSJonathan T. Looney  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232529f56eSJonathan T. Looney  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242529f56eSJonathan T. Looney  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252529f56eSJonathan T. Looney  * SUCH DAMAGE.
262529f56eSJonathan T. Looney  *
272529f56eSJonathan T. Looney  */
282529f56eSJonathan T. Looney 
292529f56eSJonathan T. Looney #include <sys/cdefs.h>
302529f56eSJonathan T. Looney __FBSDID("$FreeBSD$");
312529f56eSJonathan T. Looney 
322529f56eSJonathan T. Looney #include <sys/param.h>
33adc56f5aSEdward Tomasz Napierala #include <sys/arb.h>
342529f56eSJonathan T. Looney #include <sys/kernel.h>
352529f56eSJonathan T. Looney #include <sys/lock.h>
362529f56eSJonathan T. Looney #include <sys/malloc.h>
372529f56eSJonathan T. Looney #include <sys/mutex.h>
38adc56f5aSEdward Tomasz Napierala #include <sys/qmath.h>
392529f56eSJonathan T. Looney #include <sys/queue.h>
402529f56eSJonathan T. Looney #include <sys/refcount.h>
412529f56eSJonathan T. Looney #include <sys/rwlock.h>
422529f56eSJonathan T. Looney #include <sys/socket.h>
432529f56eSJonathan T. Looney #include <sys/socketvar.h>
442529f56eSJonathan T. Looney #include <sys/sysctl.h>
452529f56eSJonathan T. Looney #include <sys/tree.h>
46a9a08eceSRandall Stewart #include <sys/stats.h> /* Must come after qmath.h and tree.h */
472529f56eSJonathan T. Looney #include <sys/counter.h>
482529f56eSJonathan T. Looney 
492529f56eSJonathan T. Looney #include <dev/tcp_log/tcp_log_dev.h>
502529f56eSJonathan T. Looney 
512529f56eSJonathan T. Looney #include <net/if.h>
522529f56eSJonathan T. Looney #include <net/if_var.h>
532529f56eSJonathan T. Looney #include <net/vnet.h>
542529f56eSJonathan T. Looney 
552529f56eSJonathan T. Looney #include <netinet/in.h>
562529f56eSJonathan T. Looney #include <netinet/in_pcb.h>
572529f56eSJonathan T. Looney #include <netinet/in_var.h>
582529f56eSJonathan T. Looney #include <netinet/tcp_var.h>
592529f56eSJonathan T. Looney #include <netinet/tcp_log_buf.h>
602529f56eSJonathan T. Looney 
612529f56eSJonathan T. Looney /* Default expiry time */
622529f56eSJonathan T. Looney #define	TCP_LOG_EXPIRE_TIME	((sbintime_t)60 * SBT_1S)
632529f56eSJonathan T. Looney 
642529f56eSJonathan T. Looney /* Max interval at which to run the expiry timer */
652529f56eSJonathan T. Looney #define	TCP_LOG_EXPIRE_INTVL	((sbintime_t)5 * SBT_1S)
662529f56eSJonathan T. Looney 
672529f56eSJonathan T. Looney bool	tcp_log_verbose;
688c47d8f5SAlan Somers static uma_zone_t tcp_log_id_bucket_zone, tcp_log_id_node_zone, tcp_log_zone;
692529f56eSJonathan T. Looney static int	tcp_log_session_limit = TCP_LOG_BUF_DEFAULT_SESSION_LIMIT;
702529f56eSJonathan T. Looney static uint32_t	tcp_log_version = TCP_LOG_BUF_VER;
712529f56eSJonathan T. Looney RB_HEAD(tcp_log_id_tree, tcp_log_id_bucket);
722529f56eSJonathan T. Looney static struct tcp_log_id_tree tcp_log_id_head;
732529f56eSJonathan T. Looney static STAILQ_HEAD(, tcp_log_id_node) tcp_log_expireq_head =
742529f56eSJonathan T. Looney     STAILQ_HEAD_INITIALIZER(tcp_log_expireq_head);
752529f56eSJonathan T. Looney static struct mtx tcp_log_expireq_mtx;
762529f56eSJonathan T. Looney static struct callout tcp_log_expireq_callout;
779959c8b9SJonathan T. Looney static u_long tcp_log_auto_ratio = 0;
789959c8b9SJonathan T. Looney static volatile u_long tcp_log_auto_ratio_cur = 0;
792529f56eSJonathan T. Looney static uint32_t tcp_log_auto_mode = TCP_LOG_STATE_TAIL;
802529f56eSJonathan T. Looney static bool tcp_log_auto_all = false;
81a9a08eceSRandall Stewart static uint32_t tcp_disable_all_bb_logs = 0;
822529f56eSJonathan T. Looney 
832529f56eSJonathan T. Looney RB_PROTOTYPE_STATIC(tcp_log_id_tree, tcp_log_id_bucket, tlb_rb, tcp_log_id_cmp)
842529f56eSJonathan T. Looney 
857029da5cSPawel Biernacki SYSCTL_NODE(_net_inet_tcp, OID_AUTO, bb, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
867029da5cSPawel Biernacki     "TCP Black Box controls");
872529f56eSJonathan T. Looney 
882529f56eSJonathan T. Looney SYSCTL_BOOL(_net_inet_tcp_bb, OID_AUTO, log_verbose, CTLFLAG_RW, &tcp_log_verbose,
892529f56eSJonathan T. Looney     0, "Force verbose logging for TCP traces");
902529f56eSJonathan T. Looney 
912529f56eSJonathan T. Looney SYSCTL_INT(_net_inet_tcp_bb, OID_AUTO, log_session_limit,
922529f56eSJonathan T. Looney     CTLFLAG_RW, &tcp_log_session_limit, 0,
932529f56eSJonathan T. Looney     "Maximum number of events maintained for each TCP session");
942529f56eSJonathan T. Looney 
952529f56eSJonathan T. Looney SYSCTL_UMA_MAX(_net_inet_tcp_bb, OID_AUTO, log_global_limit, CTLFLAG_RW,
962529f56eSJonathan T. Looney     &tcp_log_zone, "Maximum number of events maintained for all TCP sessions");
972529f56eSJonathan T. Looney 
982529f56eSJonathan T. Looney SYSCTL_UMA_CUR(_net_inet_tcp_bb, OID_AUTO, log_global_entries, CTLFLAG_RD,
992529f56eSJonathan T. Looney     &tcp_log_zone, "Current number of events maintained for all TCP sessions");
1002529f56eSJonathan T. Looney 
1012529f56eSJonathan T. Looney SYSCTL_UMA_MAX(_net_inet_tcp_bb, OID_AUTO, log_id_limit, CTLFLAG_RW,
1028c47d8f5SAlan Somers     &tcp_log_id_bucket_zone, "Maximum number of log IDs");
1032529f56eSJonathan T. Looney 
1042529f56eSJonathan T. Looney SYSCTL_UMA_CUR(_net_inet_tcp_bb, OID_AUTO, log_id_entries, CTLFLAG_RD,
1058c47d8f5SAlan Somers     &tcp_log_id_bucket_zone, "Current number of log IDs");
1062529f56eSJonathan T. Looney 
1072529f56eSJonathan T. Looney SYSCTL_UMA_MAX(_net_inet_tcp_bb, OID_AUTO, log_id_tcpcb_limit, CTLFLAG_RW,
1088c47d8f5SAlan Somers     &tcp_log_id_node_zone, "Maximum number of tcpcbs with log IDs");
1092529f56eSJonathan T. Looney 
1102529f56eSJonathan T. Looney SYSCTL_UMA_CUR(_net_inet_tcp_bb, OID_AUTO, log_id_tcpcb_entries, CTLFLAG_RD,
1118c47d8f5SAlan Somers     &tcp_log_id_node_zone, "Current number of tcpcbs with log IDs");
1122529f56eSJonathan T. Looney 
1132529f56eSJonathan T. Looney SYSCTL_U32(_net_inet_tcp_bb, OID_AUTO, log_version, CTLFLAG_RD, &tcp_log_version,
1142529f56eSJonathan T. Looney     0, "Version of log formats exported");
1152529f56eSJonathan T. Looney 
116a9a08eceSRandall Stewart SYSCTL_U32(_net_inet_tcp_bb, OID_AUTO, disable_all, CTLFLAG_RW,
117*57cc27a3SCheng Cui     &tcp_disable_all_bb_logs, 0,
118a9a08eceSRandall Stewart     "Disable all BB logging for all connections");
119a9a08eceSRandall Stewart 
1209959c8b9SJonathan T. Looney SYSCTL_ULONG(_net_inet_tcp_bb, OID_AUTO, log_auto_ratio, CTLFLAG_RW,
1212529f56eSJonathan T. Looney     &tcp_log_auto_ratio, 0, "Do auto capturing for 1 out of N sessions");
1222529f56eSJonathan T. Looney 
1232529f56eSJonathan T. Looney SYSCTL_U32(_net_inet_tcp_bb, OID_AUTO, log_auto_mode, CTLFLAG_RW,
124*57cc27a3SCheng Cui     &tcp_log_auto_mode, 0,
125*57cc27a3SCheng Cui     "Logging mode for auto-selected sessions (default is TCP_LOG_STATE_TAIL)");
1262529f56eSJonathan T. Looney 
1272529f56eSJonathan T. Looney SYSCTL_BOOL(_net_inet_tcp_bb, OID_AUTO, log_auto_all, CTLFLAG_RW,
128*57cc27a3SCheng Cui     &tcp_log_auto_all, 0,
1292529f56eSJonathan T. Looney     "Auto-select from all sessions (rather than just those with IDs)");
1302529f56eSJonathan T. Looney 
1312529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
1322529f56eSJonathan T. Looney counter_u64_t tcp_log_queued;
1332529f56eSJonathan T. Looney counter_u64_t tcp_log_que_fail1;
1342529f56eSJonathan T. Looney counter_u64_t tcp_log_que_fail2;
1352529f56eSJonathan T. Looney counter_u64_t tcp_log_que_fail3;
1362529f56eSJonathan T. Looney counter_u64_t tcp_log_que_fail4;
1372529f56eSJonathan T. Looney counter_u64_t tcp_log_que_fail5;
1382529f56eSJonathan T. Looney counter_u64_t tcp_log_que_copyout;
1392529f56eSJonathan T. Looney counter_u64_t tcp_log_que_read;
1402529f56eSJonathan T. Looney counter_u64_t tcp_log_que_freed;
1412529f56eSJonathan T. Looney 
1422529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, queued, CTLFLAG_RD,
1432529f56eSJonathan T. Looney     &tcp_log_queued, "Number of entries queued");
1442529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, fail1, CTLFLAG_RD,
1452529f56eSJonathan T. Looney     &tcp_log_que_fail1, "Number of entries queued but fail 1");
1462529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, fail2, CTLFLAG_RD,
1472529f56eSJonathan T. Looney     &tcp_log_que_fail2, "Number of entries queued but fail 2");
1482529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, fail3, CTLFLAG_RD,
1492529f56eSJonathan T. Looney     &tcp_log_que_fail3, "Number of entries queued but fail 3");
1502529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, fail4, CTLFLAG_RD,
1512529f56eSJonathan T. Looney     &tcp_log_que_fail4, "Number of entries queued but fail 4");
1522529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, fail5, CTLFLAG_RD,
1532529f56eSJonathan T. Looney     &tcp_log_que_fail5, "Number of entries queued but fail 4");
1542529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, copyout, CTLFLAG_RD,
1552529f56eSJonathan T. Looney     &tcp_log_que_copyout, "Number of entries copied out");
1562529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, read, CTLFLAG_RD,
1572529f56eSJonathan T. Looney     &tcp_log_que_read, "Number of entries read from the queue");
1582529f56eSJonathan T. Looney SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, freed, CTLFLAG_RD,
1592529f56eSJonathan T. Looney     &tcp_log_que_freed, "Number of entries freed after reading");
1602529f56eSJonathan T. Looney #endif
1612529f56eSJonathan T. Looney 
1622529f56eSJonathan T. Looney #ifdef INVARIANTS
1632529f56eSJonathan T. Looney #define	TCPLOG_DEBUG_RINGBUF
1642529f56eSJonathan T. Looney #endif
165a9a08eceSRandall Stewart /* Number of requests to consider a PBCID "active". */
166a9a08eceSRandall Stewart #define	ACTIVE_REQUEST_COUNT	10
167a9a08eceSRandall Stewart 
168a9a08eceSRandall Stewart /* Statistic tracking for "active" PBCIDs. */
169a9a08eceSRandall Stewart static counter_u64_t tcp_log_pcb_ids_cur;
170a9a08eceSRandall Stewart static counter_u64_t tcp_log_pcb_ids_tot;
171a9a08eceSRandall Stewart 
172a9a08eceSRandall Stewart SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, pcb_ids_cur, CTLFLAG_RD,
173a9a08eceSRandall Stewart     &tcp_log_pcb_ids_cur, "Number of pcb IDs allocated in the system");
174a9a08eceSRandall Stewart SYSCTL_COUNTER_U64(_net_inet_tcp_bb, OID_AUTO, pcb_ids_tot, CTLFLAG_RD,
175a9a08eceSRandall Stewart     &tcp_log_pcb_ids_tot, "Total number of pcb IDs that have been allocated");
1762529f56eSJonathan T. Looney 
1772529f56eSJonathan T. Looney struct tcp_log_mem
1782529f56eSJonathan T. Looney {
1792529f56eSJonathan T. Looney 	STAILQ_ENTRY(tcp_log_mem) tlm_queue;
1802529f56eSJonathan T. Looney 	struct tcp_log_buffer	tlm_buf;
1812529f56eSJonathan T. Looney 	struct tcp_log_verbose	tlm_v;
1822529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_RINGBUF
1832529f56eSJonathan T. Looney 	volatile int		tlm_refcnt;
1842529f56eSJonathan T. Looney #endif
1852529f56eSJonathan T. Looney };
1862529f56eSJonathan T. Looney 
1872529f56eSJonathan T. Looney /* 60 bytes for the header, + 16 bytes for padding */
1882529f56eSJonathan T. Looney static uint8_t	zerobuf[76];
1892529f56eSJonathan T. Looney 
1902529f56eSJonathan T. Looney /*
1912529f56eSJonathan T. Looney  * Lock order:
1922529f56eSJonathan T. Looney  * 1. TCPID_TREE
1932529f56eSJonathan T. Looney  * 2. TCPID_BUCKET
1942529f56eSJonathan T. Looney  * 3. INP
1952529f56eSJonathan T. Looney  *
1962529f56eSJonathan T. Looney  * Rules:
1972529f56eSJonathan T. Looney  * A. You need a lock on the Tree to add/remove buckets.
1982529f56eSJonathan T. Looney  * B. You need a lock on the bucket to add/remove nodes from the bucket.
1992529f56eSJonathan T. Looney  * C. To change information in a node, you need the INP lock if the tln_closed
2002529f56eSJonathan T. Looney  *    field is false. Otherwise, you need the bucket lock. (Note that the
2012529f56eSJonathan T. Looney  *    tln_closed field can change at any point, so you need to recheck the
2022529f56eSJonathan T. Looney  *    entry after acquiring the INP lock.)
2032529f56eSJonathan T. Looney  * D. To remove a node from the bucket, you must have that entry locked,
2042529f56eSJonathan T. Looney  *    according to the criteria of Rule C. Also, the node must not be on
2052529f56eSJonathan T. Looney  *    the expiry queue.
2062529f56eSJonathan T. Looney  * E. The exception to C is the expiry queue fields, which are locked by
2072529f56eSJonathan T. Looney  *    the TCPLOG_EXPIREQ lock.
2082529f56eSJonathan T. Looney  *
2092529f56eSJonathan T. Looney  * Buckets have a reference count. Each node is a reference. Further,
2102529f56eSJonathan T. Looney  * other callers may add reference counts to keep a bucket from disappearing.
2112529f56eSJonathan T. Looney  * You can add a reference as long as you own a lock sufficient to keep the
2122529f56eSJonathan T. Looney  * bucket from disappearing. For example, a common use is:
2132529f56eSJonathan T. Looney  *   a. Have a locked INP, but need to lock the TCPID_BUCKET.
2142529f56eSJonathan T. Looney  *   b. Add a refcount on the bucket. (Safe because the INP lock prevents
2152529f56eSJonathan T. Looney  *      the TCPID_BUCKET from going away.)
2162529f56eSJonathan T. Looney  *   c. Drop the INP lock.
2172529f56eSJonathan T. Looney  *   d. Acquire a lock on the TCPID_BUCKET.
2182529f56eSJonathan T. Looney  *   e. Acquire a lock on the INP.
2192529f56eSJonathan T. Looney  *   f. Drop the refcount on the bucket.
2202529f56eSJonathan T. Looney  *      (At this point, the bucket may disappear.)
2212529f56eSJonathan T. Looney  *
2222529f56eSJonathan T. Looney  * Expire queue lock:
2232529f56eSJonathan T. Looney  * You can acquire this with either the bucket or INP lock. Don't reverse it.
2242529f56eSJonathan T. Looney  * When the expire code has committed to freeing a node, it resets the expiry
2252529f56eSJonathan T. Looney  * time to SBT_MAX. That is the signal to everyone else that they should
2262529f56eSJonathan T. Looney  * leave that node alone.
2272529f56eSJonathan T. Looney  */
2282529f56eSJonathan T. Looney static struct rwlock tcp_id_tree_lock;
2292529f56eSJonathan T. Looney #define	TCPID_TREE_WLOCK()		rw_wlock(&tcp_id_tree_lock)
2302529f56eSJonathan T. Looney #define	TCPID_TREE_RLOCK()		rw_rlock(&tcp_id_tree_lock)
2312529f56eSJonathan T. Looney #define	TCPID_TREE_UPGRADE()		rw_try_upgrade(&tcp_id_tree_lock)
2322529f56eSJonathan T. Looney #define	TCPID_TREE_WUNLOCK()		rw_wunlock(&tcp_id_tree_lock)
2332529f56eSJonathan T. Looney #define	TCPID_TREE_RUNLOCK()		rw_runlock(&tcp_id_tree_lock)
2342529f56eSJonathan T. Looney #define	TCPID_TREE_WLOCK_ASSERT()	rw_assert(&tcp_id_tree_lock, RA_WLOCKED)
2352529f56eSJonathan T. Looney #define	TCPID_TREE_RLOCK_ASSERT()	rw_assert(&tcp_id_tree_lock, RA_RLOCKED)
2362529f56eSJonathan T. Looney #define	TCPID_TREE_UNLOCK_ASSERT()	rw_assert(&tcp_id_tree_lock, RA_UNLOCKED)
2372529f56eSJonathan T. Looney 
2382529f56eSJonathan T. Looney #define	TCPID_BUCKET_LOCK_INIT(tlb)	mtx_init(&((tlb)->tlb_mtx), "tcp log id bucket", NULL, MTX_DEF)
2392529f56eSJonathan T. Looney #define	TCPID_BUCKET_LOCK_DESTROY(tlb)	mtx_destroy(&((tlb)->tlb_mtx))
2402529f56eSJonathan T. Looney #define	TCPID_BUCKET_LOCK(tlb)		mtx_lock(&((tlb)->tlb_mtx))
2412529f56eSJonathan T. Looney #define	TCPID_BUCKET_UNLOCK(tlb)	mtx_unlock(&((tlb)->tlb_mtx))
2422529f56eSJonathan T. Looney #define	TCPID_BUCKET_LOCK_ASSERT(tlb)	mtx_assert(&((tlb)->tlb_mtx), MA_OWNED)
2432529f56eSJonathan T. Looney #define	TCPID_BUCKET_UNLOCK_ASSERT(tlb) mtx_assert(&((tlb)->tlb_mtx), MA_NOTOWNED)
2442529f56eSJonathan T. Looney 
2452529f56eSJonathan T. Looney #define	TCPID_BUCKET_REF(tlb)		refcount_acquire(&((tlb)->tlb_refcnt))
2462529f56eSJonathan T. Looney #define	TCPID_BUCKET_UNREF(tlb)		refcount_release(&((tlb)->tlb_refcnt))
2472529f56eSJonathan T. Looney 
2482529f56eSJonathan T. Looney #define	TCPLOG_EXPIREQ_LOCK()		mtx_lock(&tcp_log_expireq_mtx)
2492529f56eSJonathan T. Looney #define	TCPLOG_EXPIREQ_UNLOCK()		mtx_unlock(&tcp_log_expireq_mtx)
2502529f56eSJonathan T. Looney 
2512529f56eSJonathan T. Looney SLIST_HEAD(tcp_log_id_head, tcp_log_id_node);
2522529f56eSJonathan T. Looney 
2532529f56eSJonathan T. Looney struct tcp_log_id_bucket
2542529f56eSJonathan T. Looney {
2552529f56eSJonathan T. Looney 	/*
2562529f56eSJonathan T. Looney 	 * tlb_id must be first. This lets us use strcmp on
2572529f56eSJonathan T. Looney 	 * (struct tcp_log_id_bucket *) and (char *) interchangeably.
2582529f56eSJonathan T. Looney 	 */
2592529f56eSJonathan T. Looney 	char				tlb_id[TCP_LOG_ID_LEN];
260a9a08eceSRandall Stewart 	char				tlb_tag[TCP_LOG_TAG_LEN];
2612529f56eSJonathan T. Looney 	RB_ENTRY(tcp_log_id_bucket)	tlb_rb;
2622529f56eSJonathan T. Looney 	struct tcp_log_id_head		tlb_head;
2632529f56eSJonathan T. Looney 	struct mtx			tlb_mtx;
2642529f56eSJonathan T. Looney 	volatile u_int			tlb_refcnt;
265a9a08eceSRandall Stewart 	volatile u_int			tlb_reqcnt;
266a9a08eceSRandall Stewart 	uint32_t			tlb_loglimit;
267a9a08eceSRandall Stewart 	uint8_t				tlb_logstate;
2682529f56eSJonathan T. Looney };
2692529f56eSJonathan T. Looney 
2702529f56eSJonathan T. Looney struct tcp_log_id_node
2712529f56eSJonathan T. Looney {
2722529f56eSJonathan T. Looney 	SLIST_ENTRY(tcp_log_id_node) tln_list;
2732529f56eSJonathan T. Looney 	STAILQ_ENTRY(tcp_log_id_node) tln_expireq; /* Locked by the expireq lock */
2742529f56eSJonathan T. Looney 	sbintime_t		tln_expiretime;	/* Locked by the expireq lock */
2752529f56eSJonathan T. Looney 
2762529f56eSJonathan T. Looney 	/*
2772529f56eSJonathan T. Looney 	 * If INP is NULL, that means the connection has closed. We've
2782529f56eSJonathan T. Looney 	 * saved the connection endpoint information and the log entries
2792529f56eSJonathan T. Looney 	 * in the tln_ie and tln_entries members. We've also saved a pointer
2802529f56eSJonathan T. Looney 	 * to the enclosing bucket here. If INP is not NULL, the information is
2812529f56eSJonathan T. Looney 	 * in the PCB and not here.
2822529f56eSJonathan T. Looney 	 */
2832529f56eSJonathan T. Looney 	struct inpcb		*tln_inp;
2842529f56eSJonathan T. Looney 	struct tcpcb		*tln_tp;
2852529f56eSJonathan T. Looney 	struct tcp_log_id_bucket *tln_bucket;
2862529f56eSJonathan T. Looney 	struct in_endpoints	tln_ie;
2872529f56eSJonathan T. Looney 	struct tcp_log_stailq	tln_entries;
2882529f56eSJonathan T. Looney 	int			tln_count;
2892529f56eSJonathan T. Looney 	volatile int		tln_closed;
2902529f56eSJonathan T. Looney 	uint8_t			tln_af;
2912529f56eSJonathan T. Looney };
2922529f56eSJonathan T. Looney 
2932529f56eSJonathan T. Looney enum tree_lock_state {
2942529f56eSJonathan T. Looney 	TREE_UNLOCKED = 0,
2952529f56eSJonathan T. Looney 	TREE_RLOCKED,
2962529f56eSJonathan T. Looney 	TREE_WLOCKED,
2972529f56eSJonathan T. Looney };
2982529f56eSJonathan T. Looney 
2992529f56eSJonathan T. Looney /* Do we want to select this session for auto-logging? */
3002529f56eSJonathan T. Looney static __inline bool
3012529f56eSJonathan T. Looney tcp_log_selectauto(void)
3022529f56eSJonathan T. Looney {
3032529f56eSJonathan T. Looney 
3042529f56eSJonathan T. Looney 	/*
3052529f56eSJonathan T. Looney 	 * If we are doing auto-capturing, figure out whether we will capture
3062529f56eSJonathan T. Looney 	 * this session.
3072529f56eSJonathan T. Looney 	 */
3082529f56eSJonathan T. Looney 	if (tcp_log_auto_ratio &&
309a9a08eceSRandall Stewart 	    (tcp_disable_all_bb_logs == 0) &&
3109959c8b9SJonathan T. Looney 	    (atomic_fetchadd_long(&tcp_log_auto_ratio_cur, 1) %
3112529f56eSJonathan T. Looney 	    tcp_log_auto_ratio) == 0)
3122529f56eSJonathan T. Looney 		return (true);
3132529f56eSJonathan T. Looney 	return (false);
3142529f56eSJonathan T. Looney }
3152529f56eSJonathan T. Looney 
3162529f56eSJonathan T. Looney static __inline int
3172529f56eSJonathan T. Looney tcp_log_id_cmp(struct tcp_log_id_bucket *a, struct tcp_log_id_bucket *b)
3182529f56eSJonathan T. Looney {
3192529f56eSJonathan T. Looney 	KASSERT(a != NULL, ("tcp_log_id_cmp: argument a is unexpectedly NULL"));
3202529f56eSJonathan T. Looney 	KASSERT(b != NULL, ("tcp_log_id_cmp: argument b is unexpectedly NULL"));
3212529f56eSJonathan T. Looney 	return strncmp(a->tlb_id, b->tlb_id, TCP_LOG_ID_LEN);
3222529f56eSJonathan T. Looney }
3232529f56eSJonathan T. Looney 
3242529f56eSJonathan T. Looney RB_GENERATE_STATIC(tcp_log_id_tree, tcp_log_id_bucket, tlb_rb, tcp_log_id_cmp)
3252529f56eSJonathan T. Looney 
3262529f56eSJonathan T. Looney static __inline void
3272529f56eSJonathan T. Looney tcp_log_id_validate_tree_lock(int tree_locked)
3282529f56eSJonathan T. Looney {
3292529f56eSJonathan T. Looney 
3302529f56eSJonathan T. Looney #ifdef INVARIANTS
3312529f56eSJonathan T. Looney 	switch (tree_locked) {
3322529f56eSJonathan T. Looney 	case TREE_WLOCKED:
3332529f56eSJonathan T. Looney 		TCPID_TREE_WLOCK_ASSERT();
3342529f56eSJonathan T. Looney 		break;
3352529f56eSJonathan T. Looney 	case TREE_RLOCKED:
3362529f56eSJonathan T. Looney 		TCPID_TREE_RLOCK_ASSERT();
3372529f56eSJonathan T. Looney 		break;
3382529f56eSJonathan T. Looney 	case TREE_UNLOCKED:
3392529f56eSJonathan T. Looney 		TCPID_TREE_UNLOCK_ASSERT();
3402529f56eSJonathan T. Looney 		break;
3412529f56eSJonathan T. Looney 	default:
3422529f56eSJonathan T. Looney 		kassert_panic("%s:%d: unknown tree lock state", __func__,
3432529f56eSJonathan T. Looney 		    __LINE__);
3442529f56eSJonathan T. Looney 	}
3452529f56eSJonathan T. Looney #endif
3462529f56eSJonathan T. Looney }
3472529f56eSJonathan T. Looney 
3482529f56eSJonathan T. Looney static __inline void
3492529f56eSJonathan T. Looney tcp_log_remove_bucket(struct tcp_log_id_bucket *tlb)
3502529f56eSJonathan T. Looney {
3512529f56eSJonathan T. Looney 
3522529f56eSJonathan T. Looney 	TCPID_TREE_WLOCK_ASSERT();
3532529f56eSJonathan T. Looney 	KASSERT(SLIST_EMPTY(&tlb->tlb_head),
3542529f56eSJonathan T. Looney 	    ("%s: Attempt to remove non-empty bucket", __func__));
3552529f56eSJonathan T. Looney 	if (RB_REMOVE(tcp_log_id_tree, &tcp_log_id_head, tlb) == NULL) {
3562529f56eSJonathan T. Looney #ifdef INVARIANTS
3572529f56eSJonathan T. Looney 		kassert_panic("%s:%d: error removing element from tree",
3582529f56eSJonathan T. Looney 			    __func__, __LINE__);
3592529f56eSJonathan T. Looney #endif
3602529f56eSJonathan T. Looney 	}
3612529f56eSJonathan T. Looney 	TCPID_BUCKET_LOCK_DESTROY(tlb);
362a9a08eceSRandall Stewart 	counter_u64_add(tcp_log_pcb_ids_cur, (int64_t)-1);
3638c47d8f5SAlan Somers 	uma_zfree(tcp_log_id_bucket_zone, tlb);
3642529f56eSJonathan T. Looney }
3652529f56eSJonathan T. Looney 
3662529f56eSJonathan T. Looney /*
3672529f56eSJonathan T. Looney  * Call with a referenced and locked bucket.
3682529f56eSJonathan T. Looney  * Will return true if the bucket was freed; otherwise, false.
3692529f56eSJonathan T. Looney  * tlb: The bucket to unreference.
3702529f56eSJonathan T. Looney  * tree_locked: A pointer to the state of the tree lock. If the tree lock
3712529f56eSJonathan T. Looney  *    state changes, the function will update it.
3722529f56eSJonathan T. Looney  * inp: If not NULL and the function needs to drop the inp lock to relock the
3732529f56eSJonathan T. Looney  *    tree, it will do so. (The caller must ensure inp will not become invalid,
3742529f56eSJonathan T. Looney  *    probably by holding a reference to it.)
3752529f56eSJonathan T. Looney  */
3762529f56eSJonathan T. Looney static bool
3772529f56eSJonathan T. Looney tcp_log_unref_bucket(struct tcp_log_id_bucket *tlb, int *tree_locked,
3782529f56eSJonathan T. Looney     struct inpcb *inp)
3792529f56eSJonathan T. Looney {
3802529f56eSJonathan T. Looney 
3812529f56eSJonathan T. Looney 	KASSERT(tlb != NULL, ("%s: called with NULL tlb", __func__));
3822529f56eSJonathan T. Looney 	KASSERT(tree_locked != NULL, ("%s: called with NULL tree_locked",
3832529f56eSJonathan T. Looney 	    __func__));
3842529f56eSJonathan T. Looney 
3852529f56eSJonathan T. Looney 	tcp_log_id_validate_tree_lock(*tree_locked);
3862529f56eSJonathan T. Looney 
3872529f56eSJonathan T. Looney 	/*
3882529f56eSJonathan T. Looney 	 * Did we hold the last reference on the tlb? If so, we may need
3892529f56eSJonathan T. Looney 	 * to free it. (Note that we can realistically only execute the
3902529f56eSJonathan T. Looney 	 * loop twice: once without a write lock and once with a write
3912529f56eSJonathan T. Looney 	 * lock.)
3922529f56eSJonathan T. Looney 	 */
3932529f56eSJonathan T. Looney 	while (TCPID_BUCKET_UNREF(tlb)) {
3942529f56eSJonathan T. Looney 		/*
3952529f56eSJonathan T. Looney 		 * We need a write lock on the tree to free this.
3962529f56eSJonathan T. Looney 		 * If we can upgrade the tree lock, this is "easy". If we
3972529f56eSJonathan T. Looney 		 * can't upgrade the tree lock, we need to do this the
3982529f56eSJonathan T. Looney 		 * "hard" way: unwind all our locks and relock everything.
3992529f56eSJonathan T. Looney 		 * In the meantime, anything could have changed. We even
4002529f56eSJonathan T. Looney 		 * need to validate that we still need to free the bucket.
4012529f56eSJonathan T. Looney 		 */
4022529f56eSJonathan T. Looney 		if (*tree_locked == TREE_RLOCKED && TCPID_TREE_UPGRADE())
4032529f56eSJonathan T. Looney 			*tree_locked = TREE_WLOCKED;
4042529f56eSJonathan T. Looney 		else if (*tree_locked != TREE_WLOCKED) {
4052529f56eSJonathan T. Looney 			TCPID_BUCKET_REF(tlb);
4062529f56eSJonathan T. Looney 			if (inp != NULL)
4072529f56eSJonathan T. Looney 				INP_WUNLOCK(inp);
4082529f56eSJonathan T. Looney 			TCPID_BUCKET_UNLOCK(tlb);
4092529f56eSJonathan T. Looney 			if (*tree_locked == TREE_RLOCKED)
4102529f56eSJonathan T. Looney 				TCPID_TREE_RUNLOCK();
4112529f56eSJonathan T. Looney 			TCPID_TREE_WLOCK();
4122529f56eSJonathan T. Looney 			*tree_locked = TREE_WLOCKED;
4132529f56eSJonathan T. Looney 			TCPID_BUCKET_LOCK(tlb);
4142529f56eSJonathan T. Looney 			if (inp != NULL)
4152529f56eSJonathan T. Looney 				INP_WLOCK(inp);
4162529f56eSJonathan T. Looney 			continue;
4172529f56eSJonathan T. Looney 		}
4182529f56eSJonathan T. Looney 
4192529f56eSJonathan T. Looney 		/*
4202529f56eSJonathan T. Looney 		 * We have an empty bucket and a write lock on the tree.
4212529f56eSJonathan T. Looney 		 * Remove the empty bucket.
4222529f56eSJonathan T. Looney 		 */
4232529f56eSJonathan T. Looney 		tcp_log_remove_bucket(tlb);
4242529f56eSJonathan T. Looney 		return (true);
4252529f56eSJonathan T. Looney 	}
4262529f56eSJonathan T. Looney 	return (false);
4272529f56eSJonathan T. Looney }
4282529f56eSJonathan T. Looney 
4292529f56eSJonathan T. Looney /*
4302529f56eSJonathan T. Looney  * Call with a locked bucket. This function will release the lock on the
4312529f56eSJonathan T. Looney  * bucket before returning.
4322529f56eSJonathan T. Looney  *
4332529f56eSJonathan T. Looney  * The caller is responsible for freeing the tp->t_lin/tln node!
4342529f56eSJonathan T. Looney  *
4352529f56eSJonathan T. Looney  * Note: one of tp or both tlb and tln must be supplied.
4362529f56eSJonathan T. Looney  *
4372529f56eSJonathan T. Looney  * inp: A pointer to the inp. If the function needs to drop the inp lock to
4382529f56eSJonathan T. Looney  *    acquire the tree write lock, it will do so. (The caller must ensure inp
4392529f56eSJonathan T. Looney  *    will not become invalid, probably by holding a reference to it.)
4402529f56eSJonathan T. Looney  * tp: A pointer to the tcpcb. (optional; if specified, tlb and tln are ignored)
4412529f56eSJonathan T. Looney  * tlb: A pointer to the bucket. (optional; ignored if tp is specified)
4422529f56eSJonathan T. Looney  * tln: A pointer to the node. (optional; ignored if tp is specified)
4432529f56eSJonathan T. Looney  * tree_locked: A pointer to the state of the tree lock. If the tree lock
4442529f56eSJonathan T. Looney  *    state changes, the function will update it.
4452529f56eSJonathan T. Looney  *
4462529f56eSJonathan T. Looney  * Will return true if the INP lock was reacquired; otherwise, false.
4472529f56eSJonathan T. Looney  */
4482529f56eSJonathan T. Looney static bool
4492529f56eSJonathan T. Looney tcp_log_remove_id_node(struct inpcb *inp, struct tcpcb *tp,
4502529f56eSJonathan T. Looney     struct tcp_log_id_bucket *tlb, struct tcp_log_id_node *tln,
4512529f56eSJonathan T. Looney     int *tree_locked)
4522529f56eSJonathan T. Looney {
4532529f56eSJonathan T. Looney 	int orig_tree_locked;
4542529f56eSJonathan T. Looney 
4552529f56eSJonathan T. Looney 	KASSERT(tp != NULL || (tlb != NULL && tln != NULL),
4562529f56eSJonathan T. Looney 	    ("%s: called with tp=%p, tlb=%p, tln=%p", __func__,
4572529f56eSJonathan T. Looney 	    tp, tlb, tln));
4582529f56eSJonathan T. Looney 	KASSERT(tree_locked != NULL, ("%s: called with NULL tree_locked",
4592529f56eSJonathan T. Looney 	    __func__));
4602529f56eSJonathan T. Looney 
4612529f56eSJonathan T. Looney 	if (tp != NULL) {
4622529f56eSJonathan T. Looney 		tlb = tp->t_lib;
4632529f56eSJonathan T. Looney 		tln = tp->t_lin;
4642529f56eSJonathan T. Looney 		KASSERT(tlb != NULL, ("%s: unexpectedly NULL tlb", __func__));
4652529f56eSJonathan T. Looney 		KASSERT(tln != NULL, ("%s: unexpectedly NULL tln", __func__));
4662529f56eSJonathan T. Looney 	}
4672529f56eSJonathan T. Looney 
4682529f56eSJonathan T. Looney 	tcp_log_id_validate_tree_lock(*tree_locked);
4692529f56eSJonathan T. Looney 	TCPID_BUCKET_LOCK_ASSERT(tlb);
4702529f56eSJonathan T. Looney 
4712529f56eSJonathan T. Looney 	/*
4722529f56eSJonathan T. Looney 	 * Remove the node, clear the log bucket and node from the TCPCB, and
4732529f56eSJonathan T. Looney 	 * decrement the bucket refcount. In the process, if this is the
4742529f56eSJonathan T. Looney 	 * last reference, the bucket will be freed.
4752529f56eSJonathan T. Looney 	 */
4762529f56eSJonathan T. Looney 	SLIST_REMOVE(&tlb->tlb_head, tln, tcp_log_id_node, tln_list);
4772529f56eSJonathan T. Looney 	if (tp != NULL) {
4782529f56eSJonathan T. Looney 		tp->t_lib = NULL;
4792529f56eSJonathan T. Looney 		tp->t_lin = NULL;
4802529f56eSJonathan T. Looney 	}
4812529f56eSJonathan T. Looney 	orig_tree_locked = *tree_locked;
4822529f56eSJonathan T. Looney 	if (!tcp_log_unref_bucket(tlb, tree_locked, inp))
4832529f56eSJonathan T. Looney 		TCPID_BUCKET_UNLOCK(tlb);
4842529f56eSJonathan T. Looney 	return (*tree_locked != orig_tree_locked);
4852529f56eSJonathan T. Looney }
4862529f56eSJonathan T. Looney 
4872529f56eSJonathan T. Looney #define	RECHECK_INP_CLEAN(cleanup)	do {			\
48853af6903SGleb Smirnoff 	if (inp->inp_flags & INP_DROPPED) {			\
4892529f56eSJonathan T. Looney 		rv = ECONNRESET;				\
4902529f56eSJonathan T. Looney 		cleanup;					\
4912529f56eSJonathan T. Looney 		goto done;					\
4922529f56eSJonathan T. Looney 	}							\
4932529f56eSJonathan T. Looney 	tp = intotcpcb(inp);					\
4942529f56eSJonathan T. Looney } while (0)
4952529f56eSJonathan T. Looney 
4962529f56eSJonathan T. Looney #define	RECHECK_INP()	RECHECK_INP_CLEAN(/* noop */)
4972529f56eSJonathan T. Looney 
4982529f56eSJonathan T. Looney static void
4992529f56eSJonathan T. Looney tcp_log_grow_tlb(char *tlb_id, struct tcpcb *tp)
5002529f56eSJonathan T. Looney {
5012529f56eSJonathan T. Looney 
5029eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
5032529f56eSJonathan T. Looney 
504adc56f5aSEdward Tomasz Napierala #ifdef STATS
5052529f56eSJonathan T. Looney 	if (V_tcp_perconn_stats_enable == 2 && tp->t_stats == NULL)
5062529f56eSJonathan T. Looney 		(void)tcp_stats_sample_rollthedice(tp, tlb_id, strlen(tlb_id));
5072529f56eSJonathan T. Looney #endif
5082529f56eSJonathan T. Looney }
5092529f56eSJonathan T. Looney 
510a9a08eceSRandall Stewart static void
511a9a08eceSRandall Stewart tcp_log_increment_reqcnt(struct tcp_log_id_bucket *tlb)
512a9a08eceSRandall Stewart {
513a9a08eceSRandall Stewart 
514a9a08eceSRandall Stewart 	atomic_fetchadd_int(&tlb->tlb_reqcnt, 1);
515a9a08eceSRandall Stewart }
516a9a08eceSRandall Stewart 
517a9a08eceSRandall Stewart /*
518a9a08eceSRandall Stewart  * Associate the specified tag with a particular TCP log ID.
519a9a08eceSRandall Stewart  * Called with INPCB locked. Returns with it unlocked.
520a9a08eceSRandall Stewart  * Returns 0 on success or EOPNOTSUPP if the connection has no TCP log ID.
521a9a08eceSRandall Stewart  */
522a9a08eceSRandall Stewart int
523a9a08eceSRandall Stewart tcp_log_set_tag(struct tcpcb *tp, char *tag)
524a9a08eceSRandall Stewart {
5259eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
526a9a08eceSRandall Stewart 	struct tcp_log_id_bucket *tlb;
527a9a08eceSRandall Stewart 	int tree_locked;
528a9a08eceSRandall Stewart 
5299eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(inp);
530a9a08eceSRandall Stewart 
531a9a08eceSRandall Stewart 	tree_locked = TREE_UNLOCKED;
532a9a08eceSRandall Stewart 	tlb = tp->t_lib;
533a9a08eceSRandall Stewart 	if (tlb == NULL) {
5349eb0e832SGleb Smirnoff 		INP_WUNLOCK(inp);
535a9a08eceSRandall Stewart 		return (EOPNOTSUPP);
536a9a08eceSRandall Stewart 	}
537a9a08eceSRandall Stewart 
538a9a08eceSRandall Stewart 	TCPID_BUCKET_REF(tlb);
5399eb0e832SGleb Smirnoff 	INP_WUNLOCK(inp);
540a9a08eceSRandall Stewart 	TCPID_BUCKET_LOCK(tlb);
541a9a08eceSRandall Stewart 	strlcpy(tlb->tlb_tag, tag, TCP_LOG_TAG_LEN);
542a9a08eceSRandall Stewart 	if (!tcp_log_unref_bucket(tlb, &tree_locked, NULL))
543a9a08eceSRandall Stewart 		TCPID_BUCKET_UNLOCK(tlb);
544a9a08eceSRandall Stewart 
545a9a08eceSRandall Stewart 	if (tree_locked == TREE_WLOCKED) {
546a9a08eceSRandall Stewart 		TCPID_TREE_WLOCK_ASSERT();
547a9a08eceSRandall Stewart 		TCPID_TREE_WUNLOCK();
548a9a08eceSRandall Stewart 	} else if (tree_locked == TREE_RLOCKED) {
549a9a08eceSRandall Stewart 		TCPID_TREE_RLOCK_ASSERT();
550a9a08eceSRandall Stewart 		TCPID_TREE_RUNLOCK();
551a9a08eceSRandall Stewart 	} else
552a9a08eceSRandall Stewart 		TCPID_TREE_UNLOCK_ASSERT();
553a9a08eceSRandall Stewart 
554a9a08eceSRandall Stewart 	return (0);
555a9a08eceSRandall Stewart }
556a9a08eceSRandall Stewart 
5572529f56eSJonathan T. Looney /*
5582529f56eSJonathan T. Looney  * Set the TCP log ID for a TCPCB.
5592529f56eSJonathan T. Looney  * Called with INPCB locked. Returns with it unlocked.
5602529f56eSJonathan T. Looney  */
5612529f56eSJonathan T. Looney int
5622529f56eSJonathan T. Looney tcp_log_set_id(struct tcpcb *tp, char *id)
5632529f56eSJonathan T. Looney {
5642529f56eSJonathan T. Looney 	struct tcp_log_id_bucket *tlb, *tmp_tlb;
5652529f56eSJonathan T. Looney 	struct tcp_log_id_node *tln;
5669eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
5672529f56eSJonathan T. Looney 	int tree_locked, rv;
5682529f56eSJonathan T. Looney 	bool bucket_locked;
5692529f56eSJonathan T. Looney 
5702529f56eSJonathan T. Looney 	tlb = NULL;
5712529f56eSJonathan T. Looney 	tln = NULL;
5722529f56eSJonathan T. Looney 	tree_locked = TREE_UNLOCKED;
5732529f56eSJonathan T. Looney 	bucket_locked = false;
5742529f56eSJonathan T. Looney 
5752529f56eSJonathan T. Looney restart:
5762529f56eSJonathan T. Looney 	INP_WLOCK_ASSERT(inp);
5772529f56eSJonathan T. Looney 
5782529f56eSJonathan T. Looney 	/* See if the ID is unchanged. */
5792529f56eSJonathan T. Looney 	if ((tp->t_lib != NULL && !strcmp(tp->t_lib->tlb_id, id)) ||
5802529f56eSJonathan T. Looney 	    (tp->t_lib == NULL && *id == 0)) {
581a9a08eceSRandall Stewart 		if (tp->t_lib != NULL) {
582a9a08eceSRandall Stewart 			tcp_log_increment_reqcnt(tp->t_lib);
583a9a08eceSRandall Stewart 			if ((tp->t_lib->tlb_logstate) &&
584a9a08eceSRandall Stewart 			    (tp->t_log_state_set == 0)) {
585a9a08eceSRandall Stewart 				/* Clone in any logging */
586a9a08eceSRandall Stewart 
587a9a08eceSRandall Stewart 				tp->t_logstate = tp->t_lib->tlb_logstate;
588a9a08eceSRandall Stewart 			}
589a9a08eceSRandall Stewart 			if ((tp->t_lib->tlb_loglimit) &&
590a9a08eceSRandall Stewart 			    (tp->t_log_state_set == 0)) {
591a9a08eceSRandall Stewart 				/* We also have a limit set */
592a9a08eceSRandall Stewart 
593a9a08eceSRandall Stewart 				tp->t_loglimit = tp->t_lib->tlb_loglimit;
594a9a08eceSRandall Stewart 			}
595a9a08eceSRandall Stewart 		}
5962529f56eSJonathan T. Looney 		rv = 0;
5972529f56eSJonathan T. Looney 		goto done;
5982529f56eSJonathan T. Looney 	}
5992529f56eSJonathan T. Looney 
6002529f56eSJonathan T. Looney 	/*
6012529f56eSJonathan T. Looney 	 * If the TCPCB had a previous ID, we need to extricate it from
6022529f56eSJonathan T. Looney 	 * the previous list.
6032529f56eSJonathan T. Looney 	 *
6042529f56eSJonathan T. Looney 	 * Drop the TCPCB lock and lock the tree and the bucket.
6052529f56eSJonathan T. Looney 	 * Because this is called in the socket context, we (theoretically)
6062529f56eSJonathan T. Looney 	 * don't need to worry about the INPCB completely going away
6072529f56eSJonathan T. Looney 	 * while we are gone.
6082529f56eSJonathan T. Looney 	 */
6092529f56eSJonathan T. Looney 	if (tp->t_lib != NULL) {
6102529f56eSJonathan T. Looney 		tlb = tp->t_lib;
6112529f56eSJonathan T. Looney 		TCPID_BUCKET_REF(tlb);
6122529f56eSJonathan T. Looney 		INP_WUNLOCK(inp);
6132529f56eSJonathan T. Looney 
6142529f56eSJonathan T. Looney 		if (tree_locked == TREE_UNLOCKED) {
6152529f56eSJonathan T. Looney 			TCPID_TREE_RLOCK();
6162529f56eSJonathan T. Looney 			tree_locked = TREE_RLOCKED;
6172529f56eSJonathan T. Looney 		}
6182529f56eSJonathan T. Looney 		TCPID_BUCKET_LOCK(tlb);
6192529f56eSJonathan T. Looney 		bucket_locked = true;
6202529f56eSJonathan T. Looney 		INP_WLOCK(inp);
6212529f56eSJonathan T. Looney 
6222529f56eSJonathan T. Looney 		/*
6232529f56eSJonathan T. Looney 		 * Unreference the bucket. If our bucket went away, it is no
6242529f56eSJonathan T. Looney 		 * longer locked or valid.
6252529f56eSJonathan T. Looney 		 */
6262529f56eSJonathan T. Looney 		if (tcp_log_unref_bucket(tlb, &tree_locked, inp)) {
6272529f56eSJonathan T. Looney 			bucket_locked = false;
6282529f56eSJonathan T. Looney 			tlb = NULL;
6292529f56eSJonathan T. Looney 		}
6302529f56eSJonathan T. Looney 
6312529f56eSJonathan T. Looney 		/* Validate the INP. */
6322529f56eSJonathan T. Looney 		RECHECK_INP();
6332529f56eSJonathan T. Looney 
6342529f56eSJonathan T. Looney 		/*
6352529f56eSJonathan T. Looney 		 * Evaluate whether the bucket changed while we were unlocked.
6362529f56eSJonathan T. Looney 		 *
6372529f56eSJonathan T. Looney 		 * Possible scenarios here:
6382529f56eSJonathan T. Looney 		 * 1. Bucket is unchanged and the same one we started with.
6392529f56eSJonathan T. Looney 		 * 2. The TCPCB no longer has a bucket and our bucket was
6402529f56eSJonathan T. Looney 		 *    freed.
6412529f56eSJonathan T. Looney 		 * 3. The TCPCB has a new bucket, whether ours was freed.
6422529f56eSJonathan T. Looney 		 * 4. The TCPCB no longer has a bucket and our bucket was
6432529f56eSJonathan T. Looney 		 *    not freed.
6442529f56eSJonathan T. Looney 		 *
6452529f56eSJonathan T. Looney 		 * In cases 2-4, we will start over. In case 1, we will
6462529f56eSJonathan T. Looney 		 * proceed here to remove the bucket.
6472529f56eSJonathan T. Looney 		 */
6482529f56eSJonathan T. Looney 		if (tlb == NULL || tp->t_lib != tlb) {
6492529f56eSJonathan T. Looney 			KASSERT(bucket_locked || tlb == NULL,
6502529f56eSJonathan T. Looney 			    ("%s: bucket_locked (%d) and tlb (%p) are "
6512529f56eSJonathan T. Looney 			    "inconsistent", __func__, bucket_locked, tlb));
6522529f56eSJonathan T. Looney 
6532529f56eSJonathan T. Looney 			if (bucket_locked) {
6542529f56eSJonathan T. Looney 				TCPID_BUCKET_UNLOCK(tlb);
6552529f56eSJonathan T. Looney 				bucket_locked = false;
6562529f56eSJonathan T. Looney 				tlb = NULL;
6572529f56eSJonathan T. Looney 			}
6582529f56eSJonathan T. Looney 			goto restart;
6592529f56eSJonathan T. Looney 		}
6602529f56eSJonathan T. Looney 
6612529f56eSJonathan T. Looney 		/*
6622529f56eSJonathan T. Looney 		 * Store the (struct tcp_log_id_node) for reuse. Then, remove
6632529f56eSJonathan T. Looney 		 * it from the bucket. In the process, we may end up relocking.
6642529f56eSJonathan T. Looney 		 * If so, we need to validate that the INP is still valid, and
6652529f56eSJonathan T. Looney 		 * the TCPCB entries match we expect.
6662529f56eSJonathan T. Looney 		 *
6672529f56eSJonathan T. Looney 		 * We will clear tlb and change the bucket_locked state just
6682529f56eSJonathan T. Looney 		 * before calling tcp_log_remove_id_node(), since that function
6692529f56eSJonathan T. Looney 		 * will unlock the bucket.
6702529f56eSJonathan T. Looney 		 */
6712529f56eSJonathan T. Looney 		if (tln != NULL)
6728c47d8f5SAlan Somers 			uma_zfree(tcp_log_id_node_zone, tln);
6732529f56eSJonathan T. Looney 		tln = tp->t_lin;
6742529f56eSJonathan T. Looney 		tlb = NULL;
6752529f56eSJonathan T. Looney 		bucket_locked = false;
6762529f56eSJonathan T. Looney 		if (tcp_log_remove_id_node(inp, tp, NULL, NULL, &tree_locked)) {
6772529f56eSJonathan T. Looney 			RECHECK_INP();
6782529f56eSJonathan T. Looney 
6792529f56eSJonathan T. Looney 			/*
6802529f56eSJonathan T. Looney 			 * If the TCPCB moved to a new bucket while we had
6812529f56eSJonathan T. Looney 			 * dropped the lock, restart.
6822529f56eSJonathan T. Looney 			 */
6832529f56eSJonathan T. Looney 			if (tp->t_lib != NULL || tp->t_lin != NULL)
6842529f56eSJonathan T. Looney 				goto restart;
6852529f56eSJonathan T. Looney 		}
6862529f56eSJonathan T. Looney 
6872529f56eSJonathan T. Looney 		/*
6882529f56eSJonathan T. Looney 		 * Yay! We successfully removed the TCPCB from its old
6892529f56eSJonathan T. Looney 		 * bucket. Phew!
6902529f56eSJonathan T. Looney 		 *
6912529f56eSJonathan T. Looney 		 * On to bigger and better things...
6922529f56eSJonathan T. Looney 		 */
6932529f56eSJonathan T. Looney 	}
6942529f56eSJonathan T. Looney 
6952529f56eSJonathan T. Looney 	/* At this point, the TCPCB should not be in any bucket. */
6962529f56eSJonathan T. Looney 	KASSERT(tp->t_lib == NULL, ("%s: tp->t_lib is not NULL", __func__));
6972529f56eSJonathan T. Looney 
6982529f56eSJonathan T. Looney 	/*
6992529f56eSJonathan T. Looney 	 * If the new ID is not empty, we need to now assign this TCPCB to a
7002529f56eSJonathan T. Looney 	 * new bucket.
7012529f56eSJonathan T. Looney 	 */
7022529f56eSJonathan T. Looney 	if (*id) {
7032529f56eSJonathan T. Looney 		/* Get a new tln, if we don't already have one to reuse. */
7042529f56eSJonathan T. Looney 		if (tln == NULL) {
7058c47d8f5SAlan Somers 			tln = uma_zalloc(tcp_log_id_node_zone,
7068c47d8f5SAlan Somers 				M_NOWAIT | M_ZERO);
7072529f56eSJonathan T. Looney 			if (tln == NULL) {
7082529f56eSJonathan T. Looney 				rv = ENOBUFS;
7092529f56eSJonathan T. Looney 				goto done;
7102529f56eSJonathan T. Looney 			}
7112529f56eSJonathan T. Looney 			tln->tln_inp = inp;
7122529f56eSJonathan T. Looney 			tln->tln_tp = tp;
7132529f56eSJonathan T. Looney 		}
7142529f56eSJonathan T. Looney 
7152529f56eSJonathan T. Looney 		/*
7162529f56eSJonathan T. Looney 		 * Drop the INP lock for a bit. We don't need it, and dropping
7172529f56eSJonathan T. Looney 		 * it prevents lock order reversals.
7182529f56eSJonathan T. Looney 		 */
7192529f56eSJonathan T. Looney 		INP_WUNLOCK(inp);
7202529f56eSJonathan T. Looney 
7212529f56eSJonathan T. Looney 		/* Make sure we have at least a read lock on the tree. */
7222529f56eSJonathan T. Looney 		tcp_log_id_validate_tree_lock(tree_locked);
7232529f56eSJonathan T. Looney 		if (tree_locked == TREE_UNLOCKED) {
7242529f56eSJonathan T. Looney 			TCPID_TREE_RLOCK();
7252529f56eSJonathan T. Looney 			tree_locked = TREE_RLOCKED;
7262529f56eSJonathan T. Looney 		}
7272529f56eSJonathan T. Looney 
7282529f56eSJonathan T. Looney refind:
7292529f56eSJonathan T. Looney 		/*
7302529f56eSJonathan T. Looney 		 * Remember that we constructed (struct tcp_log_id_node) so
7312529f56eSJonathan T. Looney 		 * we can safely cast the id to it for the purposes of finding.
7322529f56eSJonathan T. Looney 		 */
7332529f56eSJonathan T. Looney 		KASSERT(tlb == NULL, ("%s:%d tlb unexpectedly non-NULL",
7342529f56eSJonathan T. Looney 		    __func__, __LINE__));
7352529f56eSJonathan T. Looney 		tmp_tlb = RB_FIND(tcp_log_id_tree, &tcp_log_id_head,
7362529f56eSJonathan T. Looney 		    (struct tcp_log_id_bucket *) id);
7372529f56eSJonathan T. Looney 
7382529f56eSJonathan T. Looney 		/*
7392529f56eSJonathan T. Looney 		 * If we didn't find a matching bucket, we need to add a new
7402529f56eSJonathan T. Looney 		 * one. This requires a write lock. But, of course, we will
7412529f56eSJonathan T. Looney 		 * need to recheck some things when we re-acquire the lock.
7422529f56eSJonathan T. Looney 		 */
7432529f56eSJonathan T. Looney 		if (tmp_tlb == NULL && tree_locked != TREE_WLOCKED) {
7442529f56eSJonathan T. Looney 			tree_locked = TREE_WLOCKED;
7452529f56eSJonathan T. Looney 			if (!TCPID_TREE_UPGRADE()) {
7462529f56eSJonathan T. Looney 				TCPID_TREE_RUNLOCK();
7472529f56eSJonathan T. Looney 				TCPID_TREE_WLOCK();
7482529f56eSJonathan T. Looney 
7492529f56eSJonathan T. Looney 				/*
7502529f56eSJonathan T. Looney 				 * The tree may have changed while we were
7512529f56eSJonathan T. Looney 				 * unlocked.
7522529f56eSJonathan T. Looney 				 */
7532529f56eSJonathan T. Looney 				goto refind;
7542529f56eSJonathan T. Looney 			}
7552529f56eSJonathan T. Looney 		}
7562529f56eSJonathan T. Looney 
7572529f56eSJonathan T. Looney 		/* If we need to add a new bucket, do it now. */
7582529f56eSJonathan T. Looney 		if (tmp_tlb == NULL) {
7592529f56eSJonathan T. Looney 			/* Allocate new bucket. */
7608c47d8f5SAlan Somers 			tlb = uma_zalloc(tcp_log_id_bucket_zone, M_NOWAIT);
7612529f56eSJonathan T. Looney 			if (tlb == NULL) {
7622529f56eSJonathan T. Looney 				rv = ENOBUFS;
7632529f56eSJonathan T. Looney 				goto done_noinp;
7642529f56eSJonathan T. Looney 			}
765a9a08eceSRandall Stewart 			counter_u64_add(tcp_log_pcb_ids_cur, 1);
766a9a08eceSRandall Stewart 			counter_u64_add(tcp_log_pcb_ids_tot, 1);
767a9a08eceSRandall Stewart 
768a9a08eceSRandall Stewart 			if ((tcp_log_auto_all == false) &&
769a9a08eceSRandall Stewart 			    tcp_log_auto_mode &&
770a9a08eceSRandall Stewart 			    tcp_log_selectauto()) {
771a9a08eceSRandall Stewart 				/* Save off the log state */
772a9a08eceSRandall Stewart 				tlb->tlb_logstate = tcp_log_auto_mode;
773a9a08eceSRandall Stewart 			} else
774a9a08eceSRandall Stewart 				tlb->tlb_logstate = TCP_LOG_STATE_OFF;
775a9a08eceSRandall Stewart 			tlb->tlb_loglimit = 0;
776a9a08eceSRandall Stewart 			tlb->tlb_tag[0] = '\0'; /* Default to an empty tag. */
7772529f56eSJonathan T. Looney 
7782529f56eSJonathan T. Looney 			/*
7792529f56eSJonathan T. Looney 			 * Copy the ID to the bucket.
7802529f56eSJonathan T. Looney 			 * NB: Don't use strlcpy() unless you are sure
7812529f56eSJonathan T. Looney 			 * we've always validated NULL termination.
7822529f56eSJonathan T. Looney 			 *
7832529f56eSJonathan T. Looney 			 * TODO: When I'm done writing this, see if we
7842529f56eSJonathan T. Looney 			 * we have correctly validated NULL termination and
7852529f56eSJonathan T. Looney 			 * can use strlcpy(). :-)
7862529f56eSJonathan T. Looney 			 */
7872529f56eSJonathan T. Looney 			strncpy(tlb->tlb_id, id, TCP_LOG_ID_LEN - 1);
7882529f56eSJonathan T. Looney 			tlb->tlb_id[TCP_LOG_ID_LEN - 1] = '\0';
7892529f56eSJonathan T. Looney 
7902529f56eSJonathan T. Looney 			/*
7912529f56eSJonathan T. Looney 			 * Take the refcount for the first node and go ahead
7922529f56eSJonathan T. Looney 			 * and lock this. Note that we zero the tlb_mtx
7932529f56eSJonathan T. Looney 			 * structure, since 0xdeadc0de flips the right bits
7942529f56eSJonathan T. Looney 			 * for the code to think that this mutex has already
7952529f56eSJonathan T. Looney 			 * been initialized. :-(
7962529f56eSJonathan T. Looney 			 */
7972529f56eSJonathan T. Looney 			SLIST_INIT(&tlb->tlb_head);
7982529f56eSJonathan T. Looney 			refcount_init(&tlb->tlb_refcnt, 1);
799a9a08eceSRandall Stewart 			tlb->tlb_reqcnt = 1;
8002529f56eSJonathan T. Looney 			memset(&tlb->tlb_mtx, 0, sizeof(struct mtx));
8012529f56eSJonathan T. Looney 			TCPID_BUCKET_LOCK_INIT(tlb);
8022529f56eSJonathan T. Looney 			TCPID_BUCKET_LOCK(tlb);
8032529f56eSJonathan T. Looney 			bucket_locked = true;
8042529f56eSJonathan T. Looney 
8052529f56eSJonathan T. Looney #define	FREE_NEW_TLB()	do {				\
8062529f56eSJonathan T. Looney 	TCPID_BUCKET_LOCK_DESTROY(tlb);			\
8078c47d8f5SAlan Somers 	uma_zfree(tcp_log_id_bucket_zone, tlb);		\
808a9a08eceSRandall Stewart 	counter_u64_add(tcp_log_pcb_ids_cur, (int64_t)-1);	\
809a9a08eceSRandall Stewart 	counter_u64_add(tcp_log_pcb_ids_tot, (int64_t)-1);	\
8102529f56eSJonathan T. Looney 	bucket_locked = false;				\
8112529f56eSJonathan T. Looney 	tlb = NULL;					\
8122529f56eSJonathan T. Looney } while (0)
8132529f56eSJonathan T. Looney 			/*
8142529f56eSJonathan T. Looney 			 * Relock the INP and make sure we are still
8152529f56eSJonathan T. Looney 			 * unassigned.
8162529f56eSJonathan T. Looney 			 */
8172529f56eSJonathan T. Looney 			INP_WLOCK(inp);
8182529f56eSJonathan T. Looney 			RECHECK_INP_CLEAN(FREE_NEW_TLB());
8192529f56eSJonathan T. Looney 			if (tp->t_lib != NULL) {
8202529f56eSJonathan T. Looney 				FREE_NEW_TLB();
8212529f56eSJonathan T. Looney 				goto restart;
8222529f56eSJonathan T. Looney 			}
8232529f56eSJonathan T. Looney 
8242529f56eSJonathan T. Looney 			/* Add the new bucket to the tree. */
8252529f56eSJonathan T. Looney 			tmp_tlb = RB_INSERT(tcp_log_id_tree, &tcp_log_id_head,
8262529f56eSJonathan T. Looney 			    tlb);
8272529f56eSJonathan T. Looney 			KASSERT(tmp_tlb == NULL,
8282529f56eSJonathan T. Looney 			    ("%s: Unexpected conflicting bucket (%p) while "
8292529f56eSJonathan T. Looney 			    "adding new bucket (%p)", __func__, tmp_tlb, tlb));
8302529f56eSJonathan T. Looney 
8312529f56eSJonathan T. Looney 			/*
8322529f56eSJonathan T. Looney 			 * If we found a conflicting bucket, free the new
8332529f56eSJonathan T. Looney 			 * one we made and fall through to use the existing
8342529f56eSJonathan T. Looney 			 * bucket.
8352529f56eSJonathan T. Looney 			 */
8362529f56eSJonathan T. Looney 			if (tmp_tlb != NULL) {
8372529f56eSJonathan T. Looney 				FREE_NEW_TLB();
8382529f56eSJonathan T. Looney 				INP_WUNLOCK(inp);
8392529f56eSJonathan T. Looney 			}
8402529f56eSJonathan T. Looney #undef	FREE_NEW_TLB
8412529f56eSJonathan T. Looney 		}
8422529f56eSJonathan T. Looney 
8432529f56eSJonathan T. Looney 		/* If we found an existing bucket, use it. */
8442529f56eSJonathan T. Looney 		if (tmp_tlb != NULL) {
8452529f56eSJonathan T. Looney 			tlb = tmp_tlb;
8462529f56eSJonathan T. Looney 			TCPID_BUCKET_LOCK(tlb);
8472529f56eSJonathan T. Looney 			bucket_locked = true;
8482529f56eSJonathan T. Looney 
8492529f56eSJonathan T. Looney 			/*
8502529f56eSJonathan T. Looney 			 * Relock the INP and make sure we are still
8512529f56eSJonathan T. Looney 			 * unassigned.
8522529f56eSJonathan T. Looney 			 */
8532529f56eSJonathan T. Looney 			INP_UNLOCK_ASSERT(inp);
8542529f56eSJonathan T. Looney 			INP_WLOCK(inp);
8552529f56eSJonathan T. Looney 			RECHECK_INP();
8562529f56eSJonathan T. Looney 			if (tp->t_lib != NULL) {
8572529f56eSJonathan T. Looney 				TCPID_BUCKET_UNLOCK(tlb);
85880219286SRandall Stewart 				bucket_locked = false;
8592529f56eSJonathan T. Looney 				tlb = NULL;
8602529f56eSJonathan T. Looney 				goto restart;
8612529f56eSJonathan T. Looney 			}
8622529f56eSJonathan T. Looney 
8632529f56eSJonathan T. Looney 			/* Take a reference on the bucket. */
8642529f56eSJonathan T. Looney 			TCPID_BUCKET_REF(tlb);
865a9a08eceSRandall Stewart 
866a9a08eceSRandall Stewart 			/* Record the request. */
867a9a08eceSRandall Stewart 			tcp_log_increment_reqcnt(tlb);
8682529f56eSJonathan T. Looney 		}
8692529f56eSJonathan T. Looney 
8702529f56eSJonathan T. Looney 		tcp_log_grow_tlb(tlb->tlb_id, tp);
8712529f56eSJonathan T. Looney 
8722529f56eSJonathan T. Looney 		/* Add the new node to the list. */
8732529f56eSJonathan T. Looney 		SLIST_INSERT_HEAD(&tlb->tlb_head, tln, tln_list);
8742529f56eSJonathan T. Looney 		tp->t_lib = tlb;
8752529f56eSJonathan T. Looney 		tp->t_lin = tln;
876a9a08eceSRandall Stewart 		if (tp->t_lib->tlb_logstate) {
877a9a08eceSRandall Stewart 			/* Clone in any logging */
878a9a08eceSRandall Stewart 
879a9a08eceSRandall Stewart 			tp->t_logstate = tp->t_lib->tlb_logstate;
880a9a08eceSRandall Stewart 		}
881a9a08eceSRandall Stewart 		if (tp->t_lib->tlb_loglimit) {
882a9a08eceSRandall Stewart 			/* The loglimit too */
883a9a08eceSRandall Stewart 
884a9a08eceSRandall Stewart 			tp->t_loglimit = tp->t_lib->tlb_loglimit;
885a9a08eceSRandall Stewart 		}
8862529f56eSJonathan T. Looney 		tln = NULL;
8872529f56eSJonathan T. Looney 	}
8882529f56eSJonathan T. Looney 
8892529f56eSJonathan T. Looney 	rv = 0;
8902529f56eSJonathan T. Looney 
8912529f56eSJonathan T. Looney done:
8922529f56eSJonathan T. Looney 	/* Unlock things, as needed, and return. */
8932529f56eSJonathan T. Looney 	INP_WUNLOCK(inp);
8942529f56eSJonathan T. Looney done_noinp:
8952529f56eSJonathan T. Looney 	INP_UNLOCK_ASSERT(inp);
8962529f56eSJonathan T. Looney 	if (bucket_locked) {
8972529f56eSJonathan T. Looney 		TCPID_BUCKET_LOCK_ASSERT(tlb);
8982529f56eSJonathan T. Looney 		TCPID_BUCKET_UNLOCK(tlb);
8992529f56eSJonathan T. Looney 	} else if (tlb != NULL)
9002529f56eSJonathan T. Looney 		TCPID_BUCKET_UNLOCK_ASSERT(tlb);
9012529f56eSJonathan T. Looney 	if (tree_locked == TREE_WLOCKED) {
9022529f56eSJonathan T. Looney 		TCPID_TREE_WLOCK_ASSERT();
9032529f56eSJonathan T. Looney 		TCPID_TREE_WUNLOCK();
9042529f56eSJonathan T. Looney 	} else if (tree_locked == TREE_RLOCKED) {
9052529f56eSJonathan T. Looney 		TCPID_TREE_RLOCK_ASSERT();
9062529f56eSJonathan T. Looney 		TCPID_TREE_RUNLOCK();
9072529f56eSJonathan T. Looney 	} else
9082529f56eSJonathan T. Looney 		TCPID_TREE_UNLOCK_ASSERT();
9092529f56eSJonathan T. Looney 	if (tln != NULL)
9108c47d8f5SAlan Somers 		uma_zfree(tcp_log_id_node_zone, tln);
9112529f56eSJonathan T. Looney 	return (rv);
9122529f56eSJonathan T. Looney }
9132529f56eSJonathan T. Looney 
9142529f56eSJonathan T. Looney /*
9152529f56eSJonathan T. Looney  * Get the TCP log ID for a TCPCB.
9162529f56eSJonathan T. Looney  * Called with INPCB locked.
9172529f56eSJonathan T. Looney  * 'buf' must point to a buffer that is at least TCP_LOG_ID_LEN bytes long.
9182529f56eSJonathan T. Looney  * Returns number of bytes copied.
9192529f56eSJonathan T. Looney  */
9202529f56eSJonathan T. Looney size_t
9212529f56eSJonathan T. Looney tcp_log_get_id(struct tcpcb *tp, char *buf)
9222529f56eSJonathan T. Looney {
9232529f56eSJonathan T. Looney 	size_t len;
9242529f56eSJonathan T. Looney 
9259eb0e832SGleb Smirnoff 	INP_LOCK_ASSERT(tptoinpcb(tp));
9262529f56eSJonathan T. Looney 	if (tp->t_lib != NULL) {
9272529f56eSJonathan T. Looney 		len = strlcpy(buf, tp->t_lib->tlb_id, TCP_LOG_ID_LEN);
9282529f56eSJonathan T. Looney 		KASSERT(len < TCP_LOG_ID_LEN,
9292529f56eSJonathan T. Looney 		    ("%s:%d: tp->t_lib->tlb_id too long (%zu)",
9302529f56eSJonathan T. Looney 		    __func__, __LINE__, len));
9312529f56eSJonathan T. Looney 	} else {
9322529f56eSJonathan T. Looney 		*buf = '\0';
9332529f56eSJonathan T. Looney 		len = 0;
9342529f56eSJonathan T. Looney 	}
9352529f56eSJonathan T. Looney 	return (len);
9362529f56eSJonathan T. Looney }
9372529f56eSJonathan T. Looney 
9382529f56eSJonathan T. Looney /*
939a9a08eceSRandall Stewart  * Get the tag associated with the TCPCB's log ID.
940a9a08eceSRandall Stewart  * Called with INPCB locked. Returns with it unlocked.
941a9a08eceSRandall Stewart  * 'buf' must point to a buffer that is at least TCP_LOG_TAG_LEN bytes long.
942a9a08eceSRandall Stewart  * Returns number of bytes copied.
943a9a08eceSRandall Stewart  */
944a9a08eceSRandall Stewart size_t
945a9a08eceSRandall Stewart tcp_log_get_tag(struct tcpcb *tp, char *buf)
946a9a08eceSRandall Stewart {
9479eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
948a9a08eceSRandall Stewart 	struct tcp_log_id_bucket *tlb;
949a9a08eceSRandall Stewart 	size_t len;
950a9a08eceSRandall Stewart 	int tree_locked;
951a9a08eceSRandall Stewart 
9529eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(inp);
953a9a08eceSRandall Stewart 
954a9a08eceSRandall Stewart 	tree_locked = TREE_UNLOCKED;
955a9a08eceSRandall Stewart 	tlb = tp->t_lib;
956a9a08eceSRandall Stewart 
957a9a08eceSRandall Stewart 	if (tlb != NULL) {
958a9a08eceSRandall Stewart 		TCPID_BUCKET_REF(tlb);
9599eb0e832SGleb Smirnoff 		INP_WUNLOCK(inp);
960a9a08eceSRandall Stewart 		TCPID_BUCKET_LOCK(tlb);
961a9a08eceSRandall Stewart 		len = strlcpy(buf, tlb->tlb_tag, TCP_LOG_TAG_LEN);
962a9a08eceSRandall Stewart 		KASSERT(len < TCP_LOG_TAG_LEN,
963a9a08eceSRandall Stewart 		    ("%s:%d: tp->t_lib->tlb_tag too long (%zu)",
964a9a08eceSRandall Stewart 		    __func__, __LINE__, len));
965a9a08eceSRandall Stewart 		if (!tcp_log_unref_bucket(tlb, &tree_locked, NULL))
966a9a08eceSRandall Stewart 			TCPID_BUCKET_UNLOCK(tlb);
967a9a08eceSRandall Stewart 
968a9a08eceSRandall Stewart 		if (tree_locked == TREE_WLOCKED) {
969a9a08eceSRandall Stewart 			TCPID_TREE_WLOCK_ASSERT();
970a9a08eceSRandall Stewart 			TCPID_TREE_WUNLOCK();
971a9a08eceSRandall Stewart 		} else if (tree_locked == TREE_RLOCKED) {
972a9a08eceSRandall Stewart 			TCPID_TREE_RLOCK_ASSERT();
973a9a08eceSRandall Stewart 			TCPID_TREE_RUNLOCK();
974a9a08eceSRandall Stewart 		} else
975a9a08eceSRandall Stewart 			TCPID_TREE_UNLOCK_ASSERT();
976a9a08eceSRandall Stewart 	} else {
9779eb0e832SGleb Smirnoff 		INP_WUNLOCK(inp);
978a9a08eceSRandall Stewart 		*buf = '\0';
979a9a08eceSRandall Stewart 		len = 0;
980a9a08eceSRandall Stewart 	}
981a9a08eceSRandall Stewart 
982a9a08eceSRandall Stewart 	return (len);
983a9a08eceSRandall Stewart }
984a9a08eceSRandall Stewart 
985a9a08eceSRandall Stewart /*
9862529f56eSJonathan T. Looney  * Get number of connections with the same log ID.
9872529f56eSJonathan T. Looney  * Log ID is taken from given TCPCB.
9882529f56eSJonathan T. Looney  * Called with INPCB locked.
9892529f56eSJonathan T. Looney  */
9902529f56eSJonathan T. Looney u_int
9912529f56eSJonathan T. Looney tcp_log_get_id_cnt(struct tcpcb *tp)
9922529f56eSJonathan T. Looney {
9932529f56eSJonathan T. Looney 
9949eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
9952529f56eSJonathan T. Looney 	return ((tp->t_lib == NULL) ? 0 : tp->t_lib->tlb_refcnt);
9962529f56eSJonathan T. Looney }
9972529f56eSJonathan T. Looney 
9982529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_RINGBUF
9992529f56eSJonathan T. Looney /*
10002529f56eSJonathan T. Looney  * Functions/macros to increment/decrement reference count for a log
10012529f56eSJonathan T. Looney  * entry. This should catch when we do a double-free/double-remove or
10022529f56eSJonathan T. Looney  * a double-add.
10032529f56eSJonathan T. Looney  */
10042529f56eSJonathan T. Looney static inline void
10052529f56eSJonathan T. Looney _tcp_log_entry_refcnt_add(struct tcp_log_mem *log_entry, const char *func,
10062529f56eSJonathan T. Looney     int line)
10072529f56eSJonathan T. Looney {
10082529f56eSJonathan T. Looney 	int refcnt;
10092529f56eSJonathan T. Looney 
10102529f56eSJonathan T. Looney 	refcnt = atomic_fetchadd_int(&log_entry->tlm_refcnt, 1);
10112529f56eSJonathan T. Looney 	if (refcnt != 0)
10122529f56eSJonathan T. Looney 		panic("%s:%d: log_entry(%p)->tlm_refcnt is %d (expected 0)",
10132529f56eSJonathan T. Looney 		    func, line, log_entry, refcnt);
10142529f56eSJonathan T. Looney }
10152529f56eSJonathan T. Looney #define	tcp_log_entry_refcnt_add(l)	\
10162529f56eSJonathan T. Looney     _tcp_log_entry_refcnt_add((l), __func__, __LINE__)
10172529f56eSJonathan T. Looney 
10182529f56eSJonathan T. Looney static inline void
10192529f56eSJonathan T. Looney _tcp_log_entry_refcnt_rem(struct tcp_log_mem *log_entry, const char *func,
10202529f56eSJonathan T. Looney     int line)
10212529f56eSJonathan T. Looney {
10222529f56eSJonathan T. Looney 	int refcnt;
10232529f56eSJonathan T. Looney 
10242529f56eSJonathan T. Looney 	refcnt = atomic_fetchadd_int(&log_entry->tlm_refcnt, -1);
10252529f56eSJonathan T. Looney 	if (refcnt != 1)
10262529f56eSJonathan T. Looney 		panic("%s:%d: log_entry(%p)->tlm_refcnt is %d (expected 1)",
10272529f56eSJonathan T. Looney 		    func, line, log_entry, refcnt);
10282529f56eSJonathan T. Looney }
10292529f56eSJonathan T. Looney #define	tcp_log_entry_refcnt_rem(l)	\
10302529f56eSJonathan T. Looney     _tcp_log_entry_refcnt_rem((l), __func__, __LINE__)
10312529f56eSJonathan T. Looney 
10322529f56eSJonathan T. Looney #else /* !TCPLOG_DEBUG_RINGBUF */
10332529f56eSJonathan T. Looney 
10342529f56eSJonathan T. Looney #define	tcp_log_entry_refcnt_add(l)
10352529f56eSJonathan T. Looney #define	tcp_log_entry_refcnt_rem(l)
10362529f56eSJonathan T. Looney 
10372529f56eSJonathan T. Looney #endif
10382529f56eSJonathan T. Looney 
10392529f56eSJonathan T. Looney /*
10402529f56eSJonathan T. Looney  * Cleanup after removing a log entry, but only decrement the count if we
10412529f56eSJonathan T. Looney  * are running INVARIANTS.
10422529f56eSJonathan T. Looney  */
10432529f56eSJonathan T. Looney static inline void
10442529f56eSJonathan T. Looney tcp_log_free_log_common(struct tcp_log_mem *log_entry, int *count __unused)
10452529f56eSJonathan T. Looney {
10462529f56eSJonathan T. Looney 
10472529f56eSJonathan T. Looney 	uma_zfree(tcp_log_zone, log_entry);
10482529f56eSJonathan T. Looney #ifdef INVARIANTS
10492529f56eSJonathan T. Looney 	(*count)--;
10502529f56eSJonathan T. Looney 	KASSERT(*count >= 0,
10512529f56eSJonathan T. Looney 	    ("%s: count unexpectedly negative", __func__));
10522529f56eSJonathan T. Looney #endif
10532529f56eSJonathan T. Looney }
10542529f56eSJonathan T. Looney 
10552529f56eSJonathan T. Looney static void
10562529f56eSJonathan T. Looney tcp_log_free_entries(struct tcp_log_stailq *head, int *count)
10572529f56eSJonathan T. Looney {
10582529f56eSJonathan T. Looney 	struct tcp_log_mem *log_entry;
10592529f56eSJonathan T. Looney 
10602529f56eSJonathan T. Looney 	/* Free the entries. */
10612529f56eSJonathan T. Looney 	while ((log_entry = STAILQ_FIRST(head)) != NULL) {
10622529f56eSJonathan T. Looney 		STAILQ_REMOVE_HEAD(head, tlm_queue);
10632529f56eSJonathan T. Looney 		tcp_log_entry_refcnt_rem(log_entry);
10642529f56eSJonathan T. Looney 		tcp_log_free_log_common(log_entry, count);
10652529f56eSJonathan T. Looney 	}
10662529f56eSJonathan T. Looney }
10672529f56eSJonathan T. Looney 
10682529f56eSJonathan T. Looney /* Cleanup after removing a log entry. */
10692529f56eSJonathan T. Looney static inline void
10702529f56eSJonathan T. Looney tcp_log_remove_log_cleanup(struct tcpcb *tp, struct tcp_log_mem *log_entry)
10712529f56eSJonathan T. Looney {
10722529f56eSJonathan T. Looney 	uma_zfree(tcp_log_zone, log_entry);
10732529f56eSJonathan T. Looney 	tp->t_lognum--;
10742529f56eSJonathan T. Looney 	KASSERT(tp->t_lognum >= 0,
10752529f56eSJonathan T. Looney 	    ("%s: tp->t_lognum unexpectedly negative", __func__));
10762529f56eSJonathan T. Looney }
10772529f56eSJonathan T. Looney 
10782529f56eSJonathan T. Looney /* Remove a log entry from the head of a list. */
10792529f56eSJonathan T. Looney static inline void
10802529f56eSJonathan T. Looney tcp_log_remove_log_head(struct tcpcb *tp, struct tcp_log_mem *log_entry)
10812529f56eSJonathan T. Looney {
10822529f56eSJonathan T. Looney 
10832529f56eSJonathan T. Looney 	KASSERT(log_entry == STAILQ_FIRST(&tp->t_logs),
10842529f56eSJonathan T. Looney 	    ("%s: attempt to remove non-HEAD log entry", __func__));
10852529f56eSJonathan T. Looney 	STAILQ_REMOVE_HEAD(&tp->t_logs, tlm_queue);
10862529f56eSJonathan T. Looney 	tcp_log_entry_refcnt_rem(log_entry);
10872529f56eSJonathan T. Looney 	tcp_log_remove_log_cleanup(tp, log_entry);
10882529f56eSJonathan T. Looney }
10892529f56eSJonathan T. Looney 
10902529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_RINGBUF
10912529f56eSJonathan T. Looney /*
10922529f56eSJonathan T. Looney  * Initialize the log entry's reference count, which we want to
10932529f56eSJonathan T. Looney  * survive allocations.
10942529f56eSJonathan T. Looney  */
10952529f56eSJonathan T. Looney static int
10962529f56eSJonathan T. Looney tcp_log_zone_init(void *mem, int size, int flags __unused)
10972529f56eSJonathan T. Looney {
10982529f56eSJonathan T. Looney 	struct tcp_log_mem *tlm;
10992529f56eSJonathan T. Looney 
11002529f56eSJonathan T. Looney 	KASSERT(size >= sizeof(struct tcp_log_mem),
11012529f56eSJonathan T. Looney 	    ("%s: unexpectedly short (%d) allocation", __func__, size));
11022529f56eSJonathan T. Looney 	tlm = (struct tcp_log_mem *)mem;
11032529f56eSJonathan T. Looney 	tlm->tlm_refcnt = 0;
11042529f56eSJonathan T. Looney 	return (0);
11052529f56eSJonathan T. Looney }
11062529f56eSJonathan T. Looney 
11072529f56eSJonathan T. Looney /*
11082529f56eSJonathan T. Looney  * Double check that the refcnt is zero on allocation and return.
11092529f56eSJonathan T. Looney  */
11102529f56eSJonathan T. Looney static int
11112529f56eSJonathan T. Looney tcp_log_zone_ctor(void *mem, int size, void *args __unused, int flags __unused)
11122529f56eSJonathan T. Looney {
11132529f56eSJonathan T. Looney 	struct tcp_log_mem *tlm;
11142529f56eSJonathan T. Looney 
11152529f56eSJonathan T. Looney 	KASSERT(size >= sizeof(struct tcp_log_mem),
11162529f56eSJonathan T. Looney 	    ("%s: unexpectedly short (%d) allocation", __func__, size));
11172529f56eSJonathan T. Looney 	tlm = (struct tcp_log_mem *)mem;
11182529f56eSJonathan T. Looney 	if (tlm->tlm_refcnt != 0)
11192529f56eSJonathan T. Looney 		panic("%s:%d: tlm(%p)->tlm_refcnt is %d (expected 0)",
11202529f56eSJonathan T. Looney 		    __func__, __LINE__, tlm, tlm->tlm_refcnt);
11212529f56eSJonathan T. Looney 	return (0);
11222529f56eSJonathan T. Looney }
11232529f56eSJonathan T. Looney 
11242529f56eSJonathan T. Looney static void
11252529f56eSJonathan T. Looney tcp_log_zone_dtor(void *mem, int size, void *args __unused)
11262529f56eSJonathan T. Looney {
11272529f56eSJonathan T. Looney 	struct tcp_log_mem *tlm;
11282529f56eSJonathan T. Looney 
11292529f56eSJonathan T. Looney 	KASSERT(size >= sizeof(struct tcp_log_mem),
11302529f56eSJonathan T. Looney 	    ("%s: unexpectedly short (%d) allocation", __func__, size));
11312529f56eSJonathan T. Looney 	tlm = (struct tcp_log_mem *)mem;
11322529f56eSJonathan T. Looney 	if (tlm->tlm_refcnt != 0)
11332529f56eSJonathan T. Looney 		panic("%s:%d: tlm(%p)->tlm_refcnt is %d (expected 0)",
11342529f56eSJonathan T. Looney 		    __func__, __LINE__, tlm, tlm->tlm_refcnt);
11352529f56eSJonathan T. Looney }
11362529f56eSJonathan T. Looney #endif /* TCPLOG_DEBUG_RINGBUF */
11372529f56eSJonathan T. Looney 
11382529f56eSJonathan T. Looney /* Do global initialization. */
11392529f56eSJonathan T. Looney void
11402529f56eSJonathan T. Looney tcp_log_init(void)
11412529f56eSJonathan T. Looney {
11422529f56eSJonathan T. Looney 
11432529f56eSJonathan T. Looney 	tcp_log_zone = uma_zcreate("tcp_log", sizeof(struct tcp_log_mem),
11442529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_RINGBUF
11452529f56eSJonathan T. Looney 	    tcp_log_zone_ctor, tcp_log_zone_dtor, tcp_log_zone_init,
11462529f56eSJonathan T. Looney #else
11472529f56eSJonathan T. Looney 	    NULL, NULL, NULL,
11482529f56eSJonathan T. Looney #endif
11492529f56eSJonathan T. Looney 	    NULL, UMA_ALIGN_PTR, 0);
11502529f56eSJonathan T. Looney 	(void)uma_zone_set_max(tcp_log_zone, TCP_LOG_BUF_DEFAULT_GLOBAL_LIMIT);
11518c47d8f5SAlan Somers 	tcp_log_id_bucket_zone = uma_zcreate("tcp_log_id_bucket",
11522529f56eSJonathan T. Looney 	    sizeof(struct tcp_log_id_bucket), NULL, NULL, NULL, NULL,
11532529f56eSJonathan T. Looney 	    UMA_ALIGN_PTR, 0);
11548c47d8f5SAlan Somers 	tcp_log_id_node_zone = uma_zcreate("tcp_log_id_node",
11552529f56eSJonathan T. Looney 	    sizeof(struct tcp_log_id_node), NULL, NULL, NULL, NULL,
11562529f56eSJonathan T. Looney 	    UMA_ALIGN_PTR, 0);
11572529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
11582529f56eSJonathan T. Looney 	tcp_log_queued = counter_u64_alloc(M_WAITOK);
11592529f56eSJonathan T. Looney 	tcp_log_que_fail1 = counter_u64_alloc(M_WAITOK);
11602529f56eSJonathan T. Looney 	tcp_log_que_fail2 = counter_u64_alloc(M_WAITOK);
11612529f56eSJonathan T. Looney 	tcp_log_que_fail3 = counter_u64_alloc(M_WAITOK);
11622529f56eSJonathan T. Looney 	tcp_log_que_fail4 = counter_u64_alloc(M_WAITOK);
11632529f56eSJonathan T. Looney 	tcp_log_que_fail5 = counter_u64_alloc(M_WAITOK);
11642529f56eSJonathan T. Looney 	tcp_log_que_copyout = counter_u64_alloc(M_WAITOK);
11652529f56eSJonathan T. Looney 	tcp_log_que_read = counter_u64_alloc(M_WAITOK);
11662529f56eSJonathan T. Looney 	tcp_log_que_freed = counter_u64_alloc(M_WAITOK);
11672529f56eSJonathan T. Looney #endif
1168a9a08eceSRandall Stewart 	tcp_log_pcb_ids_cur = counter_u64_alloc(M_WAITOK);
1169a9a08eceSRandall Stewart 	tcp_log_pcb_ids_tot = counter_u64_alloc(M_WAITOK);
11702529f56eSJonathan T. Looney 
11712529f56eSJonathan T. Looney 	rw_init_flags(&tcp_id_tree_lock, "TCP ID tree", RW_NEW);
11722529f56eSJonathan T. Looney 	mtx_init(&tcp_log_expireq_mtx, "TCP log expireq", NULL, MTX_DEF);
11732529f56eSJonathan T. Looney 	callout_init(&tcp_log_expireq_callout, 1);
11742529f56eSJonathan T. Looney }
11752529f56eSJonathan T. Looney 
11762529f56eSJonathan T. Looney /* Do per-TCPCB initialization. */
11772529f56eSJonathan T. Looney void
11782529f56eSJonathan T. Looney tcp_log_tcpcbinit(struct tcpcb *tp)
11792529f56eSJonathan T. Looney {
11802529f56eSJonathan T. Looney 
11812529f56eSJonathan T. Looney 	/* A new TCPCB should start out zero-initialized. */
11822529f56eSJonathan T. Looney 	STAILQ_INIT(&tp->t_logs);
11832529f56eSJonathan T. Looney 
11842529f56eSJonathan T. Looney 	/*
11852529f56eSJonathan T. Looney 	 * If we are doing auto-capturing, figure out whether we will capture
11862529f56eSJonathan T. Looney 	 * this session.
11872529f56eSJonathan T. Looney 	 */
1188a9a08eceSRandall Stewart 	tp->t_loglimit = tcp_log_session_limit;
1189a9a08eceSRandall Stewart 	if ((tcp_log_auto_all == true) &&
1190a9a08eceSRandall Stewart 	    tcp_log_auto_mode &&
1191a9a08eceSRandall Stewart 	    tcp_log_selectauto()) {
11922529f56eSJonathan T. Looney 		tp->t_logstate = tcp_log_auto_mode;
11932529f56eSJonathan T. Looney 		tp->t_flags2 |= TF2_LOG_AUTO;
11942529f56eSJonathan T. Looney 	}
11952529f56eSJonathan T. Looney }
11962529f56eSJonathan T. Looney 
11972529f56eSJonathan T. Looney /* Remove entries */
11982529f56eSJonathan T. Looney static void
11992529f56eSJonathan T. Looney tcp_log_expire(void *unused __unused)
12002529f56eSJonathan T. Looney {
12012529f56eSJonathan T. Looney 	struct tcp_log_id_bucket *tlb;
12022529f56eSJonathan T. Looney 	struct tcp_log_id_node *tln;
12032529f56eSJonathan T. Looney 	sbintime_t expiry_limit;
12042529f56eSJonathan T. Looney 	int tree_locked;
12052529f56eSJonathan T. Looney 
12062529f56eSJonathan T. Looney 	TCPLOG_EXPIREQ_LOCK();
12072529f56eSJonathan T. Looney 	if (callout_pending(&tcp_log_expireq_callout)) {
12082529f56eSJonathan T. Looney 		/* Callout was reset. */
12092529f56eSJonathan T. Looney 		TCPLOG_EXPIREQ_UNLOCK();
12102529f56eSJonathan T. Looney 		return;
12112529f56eSJonathan T. Looney 	}
12122529f56eSJonathan T. Looney 
12132529f56eSJonathan T. Looney 	/*
12142529f56eSJonathan T. Looney 	 * Process entries until we reach one that expires too far in the
12152529f56eSJonathan T. Looney 	 * future. Look one second in the future.
12162529f56eSJonathan T. Looney 	 */
12172529f56eSJonathan T. Looney 	expiry_limit = getsbinuptime() + SBT_1S;
12182529f56eSJonathan T. Looney 	tree_locked = TREE_UNLOCKED;
12192529f56eSJonathan T. Looney 
12202529f56eSJonathan T. Looney 	while ((tln = STAILQ_FIRST(&tcp_log_expireq_head)) != NULL &&
12212529f56eSJonathan T. Looney 	    tln->tln_expiretime <= expiry_limit) {
12222529f56eSJonathan T. Looney 		if (!callout_active(&tcp_log_expireq_callout)) {
12232529f56eSJonathan T. Looney 			/*
12242529f56eSJonathan T. Looney 			 * Callout was stopped. I guess we should
12252529f56eSJonathan T. Looney 			 * just quit at this point.
12262529f56eSJonathan T. Looney 			 */
12272529f56eSJonathan T. Looney 			TCPLOG_EXPIREQ_UNLOCK();
12282529f56eSJonathan T. Looney 			return;
12292529f56eSJonathan T. Looney 		}
12302529f56eSJonathan T. Looney 
12312529f56eSJonathan T. Looney 		/*
12322529f56eSJonathan T. Looney 		 * Remove the node from the head of the list and unlock
12332529f56eSJonathan T. Looney 		 * the list. Change the expiry time to SBT_MAX as a signal
12342529f56eSJonathan T. Looney 		 * to other threads that we now own this.
12352529f56eSJonathan T. Looney 		 */
12362529f56eSJonathan T. Looney 		STAILQ_REMOVE_HEAD(&tcp_log_expireq_head, tln_expireq);
12372529f56eSJonathan T. Looney 		tln->tln_expiretime = SBT_MAX;
12382529f56eSJonathan T. Looney 		TCPLOG_EXPIREQ_UNLOCK();
12392529f56eSJonathan T. Looney 
12402529f56eSJonathan T. Looney 		/*
12412529f56eSJonathan T. Looney 		 * Remove the node from the bucket.
12422529f56eSJonathan T. Looney 		 */
12432529f56eSJonathan T. Looney 		tlb = tln->tln_bucket;
12442529f56eSJonathan T. Looney 		TCPID_BUCKET_LOCK(tlb);
12452529f56eSJonathan T. Looney 		if (tcp_log_remove_id_node(NULL, NULL, tlb, tln, &tree_locked)) {
12462529f56eSJonathan T. Looney 			tcp_log_id_validate_tree_lock(tree_locked);
12472529f56eSJonathan T. Looney 			if (tree_locked == TREE_WLOCKED)
12482529f56eSJonathan T. Looney 				TCPID_TREE_WUNLOCK();
12492529f56eSJonathan T. Looney 			else
12502529f56eSJonathan T. Looney 				TCPID_TREE_RUNLOCK();
12512529f56eSJonathan T. Looney 			tree_locked = TREE_UNLOCKED;
12522529f56eSJonathan T. Looney 		}
12532529f56eSJonathan T. Looney 
12542529f56eSJonathan T. Looney 		/* Drop the INP reference. */
12552529f56eSJonathan T. Looney 		INP_WLOCK(tln->tln_inp);
12562529f56eSJonathan T. Looney 		if (!in_pcbrele_wlocked(tln->tln_inp))
12572529f56eSJonathan T. Looney 			INP_WUNLOCK(tln->tln_inp);
12582529f56eSJonathan T. Looney 
12592529f56eSJonathan T. Looney 		/* Free the log records. */
12602529f56eSJonathan T. Looney 		tcp_log_free_entries(&tln->tln_entries, &tln->tln_count);
12612529f56eSJonathan T. Looney 
12622529f56eSJonathan T. Looney 		/* Free the node. */
12638c47d8f5SAlan Somers 		uma_zfree(tcp_log_id_node_zone, tln);
12642529f56eSJonathan T. Looney 
12652529f56eSJonathan T. Looney 		/* Relock the expiry queue. */
12662529f56eSJonathan T. Looney 		TCPLOG_EXPIREQ_LOCK();
12672529f56eSJonathan T. Looney 	}
12682529f56eSJonathan T. Looney 
12692529f56eSJonathan T. Looney 	/*
12702529f56eSJonathan T. Looney 	 * We've expired all the entries we can. Do we need to reschedule
12712529f56eSJonathan T. Looney 	 * ourselves?
12722529f56eSJonathan T. Looney 	 */
12732529f56eSJonathan T. Looney 	callout_deactivate(&tcp_log_expireq_callout);
12742529f56eSJonathan T. Looney 	if (tln != NULL) {
12752529f56eSJonathan T. Looney 		/*
12762529f56eSJonathan T. Looney 		 * Get max(now + TCP_LOG_EXPIRE_INTVL, tln->tln_expiretime) and
12772529f56eSJonathan T. Looney 		 * set the next callout to that. (This helps ensure we generally
12782529f56eSJonathan T. Looney 		 * run the callout no more often than desired.)
12792529f56eSJonathan T. Looney 		 */
12802529f56eSJonathan T. Looney 		expiry_limit = getsbinuptime() + TCP_LOG_EXPIRE_INTVL;
12812529f56eSJonathan T. Looney 		if (expiry_limit < tln->tln_expiretime)
12822529f56eSJonathan T. Looney 			expiry_limit = tln->tln_expiretime;
12832529f56eSJonathan T. Looney 		callout_reset_sbt(&tcp_log_expireq_callout, expiry_limit,
12842529f56eSJonathan T. Looney 		    SBT_1S, tcp_log_expire, NULL, C_ABSOLUTE);
12852529f56eSJonathan T. Looney 	}
12862529f56eSJonathan T. Looney 
12872529f56eSJonathan T. Looney 	/* We're done. */
12882529f56eSJonathan T. Looney 	TCPLOG_EXPIREQ_UNLOCK();
12892529f56eSJonathan T. Looney 	return;
12902529f56eSJonathan T. Looney }
12912529f56eSJonathan T. Looney 
12922529f56eSJonathan T. Looney /*
12932529f56eSJonathan T. Looney  * Move log data from the TCPCB to a new node. This will reset the TCPCB log
12942529f56eSJonathan T. Looney  * entries and log count; however, it will not touch other things from the
12952529f56eSJonathan T. Looney  * TCPCB (e.g. t_lin, t_lib).
12962529f56eSJonathan T. Looney  *
12972529f56eSJonathan T. Looney  * NOTE: Must hold a lock on the INP.
12982529f56eSJonathan T. Looney  */
12992529f56eSJonathan T. Looney static void
13002529f56eSJonathan T. Looney tcp_log_move_tp_to_node(struct tcpcb *tp, struct tcp_log_id_node *tln)
13012529f56eSJonathan T. Looney {
13029eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
13032529f56eSJonathan T. Looney 
13049eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(inp);
13052529f56eSJonathan T. Looney 
13069eb0e832SGleb Smirnoff 	tln->tln_ie = inp->inp_inc.inc_ie;
13079eb0e832SGleb Smirnoff 	if (inp->inp_inc.inc_flags & INC_ISIPV6)
13082529f56eSJonathan T. Looney 		tln->tln_af = AF_INET6;
13092529f56eSJonathan T. Looney 	else
13102529f56eSJonathan T. Looney 		tln->tln_af = AF_INET;
13112529f56eSJonathan T. Looney 	tln->tln_entries = tp->t_logs;
13122529f56eSJonathan T. Looney 	tln->tln_count = tp->t_lognum;
13132529f56eSJonathan T. Looney 	tln->tln_bucket = tp->t_lib;
13142529f56eSJonathan T. Looney 
13152529f56eSJonathan T. Looney 	/* Clear information from the PCB. */
13162529f56eSJonathan T. Looney 	STAILQ_INIT(&tp->t_logs);
13172529f56eSJonathan T. Looney 	tp->t_lognum = 0;
13182529f56eSJonathan T. Looney }
13192529f56eSJonathan T. Looney 
13202529f56eSJonathan T. Looney /* Do per-TCPCB cleanup */
13212529f56eSJonathan T. Looney void
13222529f56eSJonathan T. Looney tcp_log_tcpcbfini(struct tcpcb *tp)
13232529f56eSJonathan T. Looney {
13242529f56eSJonathan T. Looney 	struct tcp_log_id_node *tln, *tln_first;
13252529f56eSJonathan T. Looney 	struct tcp_log_mem *log_entry;
13262529f56eSJonathan T. Looney 	sbintime_t callouttime;
13272529f56eSJonathan T. Looney 
13289eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
13292529f56eSJonathan T. Looney 
1330a9a08eceSRandall Stewart 	TCP_LOG_EVENT(tp, NULL, NULL, NULL, TCP_LOG_CONNEND, 0, 0, NULL, false);
1331a9a08eceSRandall Stewart 
13322529f56eSJonathan T. Looney 	/*
13332529f56eSJonathan T. Looney 	 * If we were gathering packets to be automatically dumped, try to do
13342529f56eSJonathan T. Looney 	 * it now. If this succeeds, the log information in the TCPCB will be
13352529f56eSJonathan T. Looney 	 * cleared. Otherwise, we'll handle the log information as we do
13362529f56eSJonathan T. Looney 	 * for other states.
13372529f56eSJonathan T. Looney 	 */
13382529f56eSJonathan T. Looney 	switch(tp->t_logstate) {
13392529f56eSJonathan T. Looney 	case TCP_LOG_STATE_HEAD_AUTO:
13402529f56eSJonathan T. Looney 		(void)tcp_log_dump_tp_logbuf(tp, "auto-dumped from head",
13412529f56eSJonathan T. Looney 		    M_NOWAIT, false);
13422529f56eSJonathan T. Looney 		break;
13432529f56eSJonathan T. Looney 	case TCP_LOG_STATE_TAIL_AUTO:
13442529f56eSJonathan T. Looney 		(void)tcp_log_dump_tp_logbuf(tp, "auto-dumped from tail",
13452529f56eSJonathan T. Looney 		    M_NOWAIT, false);
13462529f56eSJonathan T. Looney 		break;
13472529f56eSJonathan T. Looney 	case TCP_LOG_STATE_CONTINUAL:
13482529f56eSJonathan T. Looney 		(void)tcp_log_dump_tp_logbuf(tp, "auto-dumped from continual",
13492529f56eSJonathan T. Looney 		    M_NOWAIT, false);
13502529f56eSJonathan T. Looney 		break;
13512529f56eSJonathan T. Looney 	}
13522529f56eSJonathan T. Looney 
13532529f56eSJonathan T. Looney 	/*
13542529f56eSJonathan T. Looney 	 * There are two ways we could keep logs: per-socket or per-ID. If
13552529f56eSJonathan T. Looney 	 * we are tracking logs with an ID, then the logs survive the
13562529f56eSJonathan T. Looney 	 * destruction of the TCPCB.
13572529f56eSJonathan T. Looney 	 *
13582529f56eSJonathan T. Looney 	 * If the TCPCB is associated with an ID node, move the logs from the
13592529f56eSJonathan T. Looney 	 * TCPCB to the ID node. In theory, this is safe, for reasons which I
13602529f56eSJonathan T. Looney 	 * will now explain for my own benefit when I next need to figure out
13612529f56eSJonathan T. Looney 	 * this code. :-)
13622529f56eSJonathan T. Looney 	 *
13632529f56eSJonathan T. Looney 	 * We own the INP lock. Therefore, no one else can change the contents
13642529f56eSJonathan T. Looney 	 * of this node (Rule C). Further, no one can remove this node from
13652529f56eSJonathan T. Looney 	 * the bucket while we hold the lock (Rule D). Basically, no one can
13662529f56eSJonathan T. Looney 	 * mess with this node. That leaves two states in which we could be:
13672529f56eSJonathan T. Looney 	 *
13682529f56eSJonathan T. Looney 	 * 1. Another thread is currently waiting to acquire the INP lock, with
13692529f56eSJonathan T. Looney 	 *    plans to do something with this node. When we drop the INP lock,
13702529f56eSJonathan T. Looney 	 *    they will have a chance to do that. They will recheck the
13712529f56eSJonathan T. Looney 	 *    tln_closed field (see note to Rule C) and then acquire the
13722529f56eSJonathan T. Looney 	 *    bucket lock before proceeding further.
13732529f56eSJonathan T. Looney 	 *
13742529f56eSJonathan T. Looney 	 * 2. Another thread will try to acquire a lock at some point in the
13752529f56eSJonathan T. Looney 	 *    future. If they try to acquire a lock before we set the
13762529f56eSJonathan T. Looney 	 *    tln_closed field, they will follow state #1. If they try to
13772529f56eSJonathan T. Looney 	 *    acquire a lock after we set the tln_closed field, they will be
13782529f56eSJonathan T. Looney 	 *    able to make changes to the node, at will, following Rule C.
13792529f56eSJonathan T. Looney 	 *
13802529f56eSJonathan T. Looney 	 * Therefore, we currently own this node and can make any changes
13812529f56eSJonathan T. Looney 	 * we want. But, as soon as we set the tln_closed field to true, we
13822529f56eSJonathan T. Looney 	 * have effectively dropped our lock on the node. (For this reason, we
13832529f56eSJonathan T. Looney 	 * also need to make sure our writes are ordered correctly. An atomic
13842529f56eSJonathan T. Looney 	 * operation with "release" semantics should be sufficient.)
13852529f56eSJonathan T. Looney 	 */
13862529f56eSJonathan T. Looney 
13872529f56eSJonathan T. Looney 	if (tp->t_lin != NULL) {
13889eb0e832SGleb Smirnoff 		struct inpcb *inp = tptoinpcb(tp);
13899eb0e832SGleb Smirnoff 
13902529f56eSJonathan T. Looney 		/* Copy the relevant information to the log entry. */
13912529f56eSJonathan T. Looney 		tln = tp->t_lin;
13929eb0e832SGleb Smirnoff 		KASSERT(tln->tln_inp == inp,
13939eb0e832SGleb Smirnoff 		    ("%s: Mismatched inp (tln->tln_inp=%p, tp inpcb=%p)",
13949eb0e832SGleb Smirnoff 		    __func__, tln->tln_inp, inp));
13952529f56eSJonathan T. Looney 		tcp_log_move_tp_to_node(tp, tln);
13962529f56eSJonathan T. Looney 
13972529f56eSJonathan T. Looney 		/* Clear information from the PCB. */
13982529f56eSJonathan T. Looney 		tp->t_lin = NULL;
13992529f56eSJonathan T. Looney 		tp->t_lib = NULL;
14002529f56eSJonathan T. Looney 
14012529f56eSJonathan T. Looney 		/*
14022529f56eSJonathan T. Looney 		 * Take a reference on the INP. This ensures that the INP
14032529f56eSJonathan T. Looney 		 * remains valid while the node is on the expiry queue. This
14042529f56eSJonathan T. Looney 		 * ensures the INP is valid for other threads that may be
14052529f56eSJonathan T. Looney 		 * racing to lock this node when we move it to the expire
14062529f56eSJonathan T. Looney 		 * queue.
14072529f56eSJonathan T. Looney 		 */
14089eb0e832SGleb Smirnoff 		in_pcbref(inp);
14092529f56eSJonathan T. Looney 
14102529f56eSJonathan T. Looney 		/*
14112529f56eSJonathan T. Looney 		 * Store the entry on the expiry list. The exact behavior
14122529f56eSJonathan T. Looney 		 * depends on whether we have entries to keep. If so, we
14132529f56eSJonathan T. Looney 		 * put the entry at the tail of the list and expire in
14142529f56eSJonathan T. Looney 		 * TCP_LOG_EXPIRE_TIME. Otherwise, we expire "now" and put
14152529f56eSJonathan T. Looney 		 * the entry at the head of the list. (Handling the cleanup
14162529f56eSJonathan T. Looney 		 * via the expiry timer lets us avoid locking messy-ness here.)
14172529f56eSJonathan T. Looney 		 */
14182529f56eSJonathan T. Looney 		tln->tln_expiretime = getsbinuptime();
14192529f56eSJonathan T. Looney 		TCPLOG_EXPIREQ_LOCK();
14202529f56eSJonathan T. Looney 		if (tln->tln_count) {
14212529f56eSJonathan T. Looney 			tln->tln_expiretime += TCP_LOG_EXPIRE_TIME;
14222529f56eSJonathan T. Looney 			if (STAILQ_EMPTY(&tcp_log_expireq_head) &&
14232529f56eSJonathan T. Looney 			    !callout_active(&tcp_log_expireq_callout)) {
14242529f56eSJonathan T. Looney 				/*
14252529f56eSJonathan T. Looney 				 * We are adding the first entry and a callout
14262529f56eSJonathan T. Looney 				 * is not currently scheduled; therefore, we
14272529f56eSJonathan T. Looney 				 * need to schedule one.
14282529f56eSJonathan T. Looney 				 */
14292529f56eSJonathan T. Looney 				callout_reset_sbt(&tcp_log_expireq_callout,
14302529f56eSJonathan T. Looney 				    tln->tln_expiretime, SBT_1S, tcp_log_expire,
14312529f56eSJonathan T. Looney 				    NULL, C_ABSOLUTE);
14322529f56eSJonathan T. Looney 			}
14332529f56eSJonathan T. Looney 			STAILQ_INSERT_TAIL(&tcp_log_expireq_head, tln,
14342529f56eSJonathan T. Looney 			    tln_expireq);
14352529f56eSJonathan T. Looney 		} else {
14362529f56eSJonathan T. Looney 			callouttime = tln->tln_expiretime +
14372529f56eSJonathan T. Looney 			    TCP_LOG_EXPIRE_INTVL;
14382529f56eSJonathan T. Looney 			tln_first = STAILQ_FIRST(&tcp_log_expireq_head);
14392529f56eSJonathan T. Looney 
14402529f56eSJonathan T. Looney 			if ((tln_first == NULL ||
14412529f56eSJonathan T. Looney 			    callouttime < tln_first->tln_expiretime) &&
14422529f56eSJonathan T. Looney 			    (callout_pending(&tcp_log_expireq_callout) ||
14432529f56eSJonathan T. Looney 			    !callout_active(&tcp_log_expireq_callout))) {
14442529f56eSJonathan T. Looney 				/*
14452529f56eSJonathan T. Looney 				 * The list is empty, or we want to run the
14462529f56eSJonathan T. Looney 				 * expire code before the first entry's timer
14472529f56eSJonathan T. Looney 				 * fires. Also, we are in a case where a callout
14482529f56eSJonathan T. Looney 				 * is not actively running. We want to reset
14492529f56eSJonathan T. Looney 				 * the callout to occur sooner.
14502529f56eSJonathan T. Looney 				 */
14512529f56eSJonathan T. Looney 				callout_reset_sbt(&tcp_log_expireq_callout,
14522529f56eSJonathan T. Looney 				    callouttime, SBT_1S, tcp_log_expire, NULL,
14532529f56eSJonathan T. Looney 				    C_ABSOLUTE);
14542529f56eSJonathan T. Looney 			}
14552529f56eSJonathan T. Looney 
14562529f56eSJonathan T. Looney 			/*
14572529f56eSJonathan T. Looney 			 * Insert to the head, or just after the head, as
14582529f56eSJonathan T. Looney 			 * appropriate. (This might result in small
14592529f56eSJonathan T. Looney 			 * mis-orderings as a bunch of "expire now" entries
14602529f56eSJonathan T. Looney 			 * gather at the start of the list, but that should
14612529f56eSJonathan T. Looney 			 * not produce big problems, since the expire timer
14622529f56eSJonathan T. Looney 			 * will walk through all of them.)
14632529f56eSJonathan T. Looney 			 */
14642529f56eSJonathan T. Looney 			if (tln_first == NULL ||
14652529f56eSJonathan T. Looney 			    tln->tln_expiretime < tln_first->tln_expiretime)
14662529f56eSJonathan T. Looney 				STAILQ_INSERT_HEAD(&tcp_log_expireq_head, tln,
14672529f56eSJonathan T. Looney 				    tln_expireq);
14682529f56eSJonathan T. Looney 			else
14692529f56eSJonathan T. Looney 				STAILQ_INSERT_AFTER(&tcp_log_expireq_head,
14702529f56eSJonathan T. Looney 				    tln_first, tln, tln_expireq);
14712529f56eSJonathan T. Looney 		}
14722529f56eSJonathan T. Looney 		TCPLOG_EXPIREQ_UNLOCK();
14732529f56eSJonathan T. Looney 
14742529f56eSJonathan T. Looney 		/*
14752529f56eSJonathan T. Looney 		 * We are done messing with the tln. After this point, we
14762529f56eSJonathan T. Looney 		 * can't touch it. (Note that the "release" semantics should
14772529f56eSJonathan T. Looney 		 * be included with the TCPLOG_EXPIREQ_UNLOCK() call above.
14782529f56eSJonathan T. Looney 		 * Therefore, they should be unnecessary here. However, it
14792529f56eSJonathan T. Looney 		 * seems like a good idea to include them anyway, since we
14802529f56eSJonathan T. Looney 		 * really are releasing a lock here.)
14812529f56eSJonathan T. Looney 		 */
14822529f56eSJonathan T. Looney 		atomic_store_rel_int(&tln->tln_closed, 1);
14832529f56eSJonathan T. Looney 	} else {
14842529f56eSJonathan T. Looney 		/* Remove log entries. */
14852529f56eSJonathan T. Looney 		while ((log_entry = STAILQ_FIRST(&tp->t_logs)) != NULL)
14862529f56eSJonathan T. Looney 			tcp_log_remove_log_head(tp, log_entry);
14872529f56eSJonathan T. Looney 		KASSERT(tp->t_lognum == 0,
14882529f56eSJonathan T. Looney 		    ("%s: After freeing entries, tp->t_lognum=%d (expected 0)",
14892529f56eSJonathan T. Looney 			__func__, tp->t_lognum));
14902529f56eSJonathan T. Looney 	}
14912529f56eSJonathan T. Looney 
14922529f56eSJonathan T. Looney 	/*
14932529f56eSJonathan T. Looney 	 * Change the log state to off (just in case anything tries to sneak
14942529f56eSJonathan T. Looney 	 * in a last-minute log).
14952529f56eSJonathan T. Looney 	 */
14962529f56eSJonathan T. Looney 	tp->t_logstate = TCP_LOG_STATE_OFF;
14972529f56eSJonathan T. Looney }
14982529f56eSJonathan T. Looney 
1499a9a08eceSRandall Stewart static void
1500a9a08eceSRandall Stewart tcp_log_purge_tp_logbuf(struct tcpcb *tp)
1501a9a08eceSRandall Stewart {
1502a9a08eceSRandall Stewart 	struct tcp_log_mem *log_entry;
1503a9a08eceSRandall Stewart 
15049eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
1505a9a08eceSRandall Stewart 	if (tp->t_lognum == 0)
1506a9a08eceSRandall Stewart 		return;
1507a9a08eceSRandall Stewart 
1508a9a08eceSRandall Stewart 	while ((log_entry = STAILQ_FIRST(&tp->t_logs)) != NULL)
1509a9a08eceSRandall Stewart 		tcp_log_remove_log_head(tp, log_entry);
1510a9a08eceSRandall Stewart 	KASSERT(tp->t_lognum == 0,
1511a9a08eceSRandall Stewart 		("%s: After freeing entries, tp->t_lognum=%d (expected 0)",
1512a9a08eceSRandall Stewart 		 __func__, tp->t_lognum));
1513a9a08eceSRandall Stewart 	tp->t_logstate = TCP_LOG_STATE_OFF;
1514a9a08eceSRandall Stewart }
1515a9a08eceSRandall Stewart 
15162529f56eSJonathan T. Looney /*
15172529f56eSJonathan T. Looney  * This logs an event for a TCP socket. Normally, this is called via
15182529f56eSJonathan T. Looney  * TCP_LOG_EVENT or TCP_LOG_EVENT_VERBOSE. See the documentation for
15192529f56eSJonathan T. Looney  * TCP_LOG_EVENT().
15202529f56eSJonathan T. Looney  */
15212529f56eSJonathan T. Looney 
15222529f56eSJonathan T. Looney struct tcp_log_buffer *
15232529f56eSJonathan T. Looney tcp_log_event_(struct tcpcb *tp, struct tcphdr *th, struct sockbuf *rxbuf,
15242529f56eSJonathan T. Looney     struct sockbuf *txbuf, uint8_t eventid, int errornum, uint32_t len,
15252529f56eSJonathan T. Looney     union tcp_log_stackspecific *stackinfo, int th_hostorder,
15262529f56eSJonathan T. Looney     const char *output_caller, const char *func, int line, const struct timeval *itv)
15272529f56eSJonathan T. Looney {
15282529f56eSJonathan T. Looney 	struct tcp_log_mem *log_entry;
15292529f56eSJonathan T. Looney 	struct tcp_log_buffer *log_buf;
15302529f56eSJonathan T. Looney 	int attempt_count = 0;
15312529f56eSJonathan T. Looney 	struct tcp_log_verbose *log_verbose;
15322529f56eSJonathan T. Looney 	uint32_t logsn;
15332529f56eSJonathan T. Looney 
15342529f56eSJonathan T. Looney 	KASSERT((func == NULL && line == 0) || (func != NULL && line > 0),
15352529f56eSJonathan T. Looney 	    ("%s called with inconsistent func (%p) and line (%d) arguments",
15362529f56eSJonathan T. Looney 		__func__, func, line));
15372529f56eSJonathan T. Looney 
15389eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
1539a9a08eceSRandall Stewart 	if (tcp_disable_all_bb_logs) {
1540a9a08eceSRandall Stewart 		/*
1541a9a08eceSRandall Stewart 		 * The global shutdown logging
1542a9a08eceSRandall Stewart 		 * switch has been thrown. Call
1543a9a08eceSRandall Stewart 		 * the purge function that frees
1544a9a08eceSRandall Stewart 		 * purges out the logs and
1545a9a08eceSRandall Stewart 		 * turns off logging.
1546a9a08eceSRandall Stewart 		 */
1547a9a08eceSRandall Stewart 		tcp_log_purge_tp_logbuf(tp);
1548a9a08eceSRandall Stewart 		return (NULL);
1549a9a08eceSRandall Stewart 	}
15502529f56eSJonathan T. Looney 	KASSERT(tp->t_logstate == TCP_LOG_STATE_HEAD ||
15512529f56eSJonathan T. Looney 	    tp->t_logstate == TCP_LOG_STATE_TAIL ||
15522529f56eSJonathan T. Looney 	    tp->t_logstate == TCP_LOG_STATE_CONTINUAL ||
15532529f56eSJonathan T. Looney 	    tp->t_logstate == TCP_LOG_STATE_HEAD_AUTO ||
15542529f56eSJonathan T. Looney 	    tp->t_logstate == TCP_LOG_STATE_TAIL_AUTO,
15552529f56eSJonathan T. Looney 	    ("%s called with unexpected tp->t_logstate (%d)", __func__,
15562529f56eSJonathan T. Looney 		tp->t_logstate));
15572529f56eSJonathan T. Looney 
15582529f56eSJonathan T. Looney 	/*
15592529f56eSJonathan T. Looney 	 * Get the serial number. We do this early so it will
15602529f56eSJonathan T. Looney 	 * increment even if we end up skipping the log entry for some
15612529f56eSJonathan T. Looney 	 * reason.
15622529f56eSJonathan T. Looney 	 */
15632529f56eSJonathan T. Looney 	logsn = tp->t_logsn++;
15642529f56eSJonathan T. Looney 
15652529f56eSJonathan T. Looney 	/*
15662529f56eSJonathan T. Looney 	 * Can we get a new log entry? If so, increment the lognum counter
15672529f56eSJonathan T. Looney 	 * here.
15682529f56eSJonathan T. Looney 	 */
15692529f56eSJonathan T. Looney retry:
1570a9a08eceSRandall Stewart 	if (tp->t_lognum < tp->t_loglimit) {
15712529f56eSJonathan T. Looney 		if ((log_entry = uma_zalloc(tcp_log_zone, M_NOWAIT)) != NULL)
15722529f56eSJonathan T. Looney 			tp->t_lognum++;
15732529f56eSJonathan T. Looney 	} else
15742529f56eSJonathan T. Looney 		log_entry = NULL;
15752529f56eSJonathan T. Looney 
15762529f56eSJonathan T. Looney 	/* Do we need to try to reuse? */
15772529f56eSJonathan T. Looney 	if (log_entry == NULL) {
15782529f56eSJonathan T. Looney 		/*
15792529f56eSJonathan T. Looney 		 * Sacrifice auto-logged sessions without a log ID if
15802529f56eSJonathan T. Looney 		 * tcp_log_auto_all is false. (If they don't have a log
15812529f56eSJonathan T. Looney 		 * ID by now, it is probable that either they won't get one
15822529f56eSJonathan T. Looney 		 * or we are resource-constrained.)
15832529f56eSJonathan T. Looney 		 */
15842529f56eSJonathan T. Looney 		if (tp->t_lib == NULL && (tp->t_flags2 & TF2_LOG_AUTO) &&
15852529f56eSJonathan T. Looney 		    !tcp_log_auto_all) {
15862529f56eSJonathan T. Looney 			if (tcp_log_state_change(tp, TCP_LOG_STATE_CLEAR)) {
15872529f56eSJonathan T. Looney #ifdef INVARIANTS
15882529f56eSJonathan T. Looney 				panic("%s:%d: tcp_log_state_change() failed "
15892529f56eSJonathan T. Looney 				    "to set tp %p to TCP_LOG_STATE_CLEAR",
15902529f56eSJonathan T. Looney 				    __func__, __LINE__, tp);
15912529f56eSJonathan T. Looney #endif
15922529f56eSJonathan T. Looney 				tp->t_logstate = TCP_LOG_STATE_OFF;
15932529f56eSJonathan T. Looney 			}
15942529f56eSJonathan T. Looney 			return (NULL);
15952529f56eSJonathan T. Looney 		}
15962529f56eSJonathan T. Looney 		/*
15972529f56eSJonathan T. Looney 		 * If we are in TCP_LOG_STATE_HEAD_AUTO state, try to dump
15982529f56eSJonathan T. Looney 		 * the buffers. If successful, deactivate tracing. Otherwise,
15992529f56eSJonathan T. Looney 		 * leave it active so we will retry.
16002529f56eSJonathan T. Looney 		 */
16012529f56eSJonathan T. Looney 		if (tp->t_logstate == TCP_LOG_STATE_HEAD_AUTO &&
16022529f56eSJonathan T. Looney 		    !tcp_log_dump_tp_logbuf(tp, "auto-dumped from head",
16032529f56eSJonathan T. Looney 		    M_NOWAIT, false)) {
16042529f56eSJonathan T. Looney 			tp->t_logstate = TCP_LOG_STATE_OFF;
16052529f56eSJonathan T. Looney 			return(NULL);
16062529f56eSJonathan T. Looney 		} else if ((tp->t_logstate == TCP_LOG_STATE_CONTINUAL) &&
16072529f56eSJonathan T. Looney 		    !tcp_log_dump_tp_logbuf(tp, "auto-dumped from continual",
16082529f56eSJonathan T. Looney 		    M_NOWAIT, false)) {
16092529f56eSJonathan T. Looney 			if (attempt_count == 0) {
16102529f56eSJonathan T. Looney 				attempt_count++;
16112529f56eSJonathan T. Looney 				goto retry;
16122529f56eSJonathan T. Looney 			}
16132529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
16142529f56eSJonathan T. Looney 			counter_u64_add(tcp_log_que_fail4, 1);
16152529f56eSJonathan T. Looney #endif
16162529f56eSJonathan T. Looney 			return(NULL);
16172529f56eSJonathan T. Looney 		} else if (tp->t_logstate == TCP_LOG_STATE_HEAD_AUTO)
16182529f56eSJonathan T. Looney 			return(NULL);
16192529f56eSJonathan T. Looney 
16202529f56eSJonathan T. Looney 		/* If in HEAD state, just deactivate the tracing and return. */
16212529f56eSJonathan T. Looney 		if (tp->t_logstate == TCP_LOG_STATE_HEAD) {
16222529f56eSJonathan T. Looney 			tp->t_logstate = TCP_LOG_STATE_OFF;
16232529f56eSJonathan T. Looney 			return(NULL);
16242529f56eSJonathan T. Looney 		}
16252529f56eSJonathan T. Looney 
16262529f56eSJonathan T. Looney 		/*
16272529f56eSJonathan T. Looney 		 * Get a buffer to reuse. If that fails, just give up.
16282529f56eSJonathan T. Looney 		 * (We can't log anything without a buffer in which to
16292529f56eSJonathan T. Looney 		 * put it.)
16302529f56eSJonathan T. Looney 		 *
16312529f56eSJonathan T. Looney 		 * Note that we don't change the t_lognum counter
16322529f56eSJonathan T. Looney 		 * here. Because we are re-using the buffer, the total
16332529f56eSJonathan T. Looney 		 * number won't change.
16342529f56eSJonathan T. Looney 		 */
16352529f56eSJonathan T. Looney 		if ((log_entry = STAILQ_FIRST(&tp->t_logs)) == NULL)
16362529f56eSJonathan T. Looney 			return(NULL);
16372529f56eSJonathan T. Looney 		STAILQ_REMOVE_HEAD(&tp->t_logs, tlm_queue);
16382529f56eSJonathan T. Looney 		tcp_log_entry_refcnt_rem(log_entry);
16392529f56eSJonathan T. Looney 	}
16402529f56eSJonathan T. Looney 
16412529f56eSJonathan T. Looney 	KASSERT(log_entry != NULL,
16422529f56eSJonathan T. Looney 	    ("%s: log_entry unexpectedly NULL", __func__));
16432529f56eSJonathan T. Looney 
16442529f56eSJonathan T. Looney 	/* Extract the log buffer and verbose buffer pointers. */
16452529f56eSJonathan T. Looney 	log_buf = &log_entry->tlm_buf;
16462529f56eSJonathan T. Looney 	log_verbose = &log_entry->tlm_v;
16472529f56eSJonathan T. Looney 
16482529f56eSJonathan T. Looney 	/* Basic entries. */
16492529f56eSJonathan T. Looney 	if (itv == NULL)
16502529f56eSJonathan T. Looney 		getmicrouptime(&log_buf->tlb_tv);
16512529f56eSJonathan T. Looney 	else
16522529f56eSJonathan T. Looney 		memcpy(&log_buf->tlb_tv, itv, sizeof(struct timeval));
16532529f56eSJonathan T. Looney 	log_buf->tlb_ticks = ticks;
16542529f56eSJonathan T. Looney 	log_buf->tlb_sn = logsn;
16552529f56eSJonathan T. Looney 	log_buf->tlb_stackid = tp->t_fb->tfb_id;
16562529f56eSJonathan T. Looney 	log_buf->tlb_eventid = eventid;
16572529f56eSJonathan T. Looney 	log_buf->tlb_eventflags = 0;
16582529f56eSJonathan T. Looney 	log_buf->tlb_errno = errornum;
16592529f56eSJonathan T. Looney 
16602529f56eSJonathan T. Looney 	/* Socket buffers */
16612529f56eSJonathan T. Looney 	if (rxbuf != NULL) {
16622529f56eSJonathan T. Looney 		log_buf->tlb_eventflags |= TLB_FLAG_RXBUF;
16632529f56eSJonathan T. Looney 		log_buf->tlb_rxbuf.tls_sb_acc = rxbuf->sb_acc;
16642529f56eSJonathan T. Looney 		log_buf->tlb_rxbuf.tls_sb_ccc = rxbuf->sb_ccc;
16652529f56eSJonathan T. Looney 		log_buf->tlb_rxbuf.tls_sb_spare = 0;
16662529f56eSJonathan T. Looney 	}
16672529f56eSJonathan T. Looney 	if (txbuf != NULL) {
16682529f56eSJonathan T. Looney 		log_buf->tlb_eventflags |= TLB_FLAG_TXBUF;
16692529f56eSJonathan T. Looney 		log_buf->tlb_txbuf.tls_sb_acc = txbuf->sb_acc;
16702529f56eSJonathan T. Looney 		log_buf->tlb_txbuf.tls_sb_ccc = txbuf->sb_ccc;
16712529f56eSJonathan T. Looney 		log_buf->tlb_txbuf.tls_sb_spare = 0;
16722529f56eSJonathan T. Looney 	}
16732529f56eSJonathan T. Looney 	/* Copy values from tp to the log entry. */
16742529f56eSJonathan T. Looney #define	COPY_STAT(f)	log_buf->tlb_ ## f = tp->f
16752529f56eSJonathan T. Looney #define	COPY_STAT_T(f)	log_buf->tlb_ ## f = tp->t_ ## f
16762529f56eSJonathan T. Looney 	COPY_STAT_T(state);
16772529f56eSJonathan T. Looney 	COPY_STAT_T(starttime);
16782529f56eSJonathan T. Looney 	COPY_STAT(iss);
16792529f56eSJonathan T. Looney 	COPY_STAT_T(flags);
16802529f56eSJonathan T. Looney 	COPY_STAT(snd_una);
16812529f56eSJonathan T. Looney 	COPY_STAT(snd_max);
16822529f56eSJonathan T. Looney 	COPY_STAT(snd_cwnd);
16832529f56eSJonathan T. Looney 	COPY_STAT(snd_nxt);
16842529f56eSJonathan T. Looney 	COPY_STAT(snd_recover);
16852529f56eSJonathan T. Looney 	COPY_STAT(snd_wnd);
16862529f56eSJonathan T. Looney 	COPY_STAT(snd_ssthresh);
16872529f56eSJonathan T. Looney 	COPY_STAT_T(srtt);
16882529f56eSJonathan T. Looney 	COPY_STAT_T(rttvar);
16892529f56eSJonathan T. Looney 	COPY_STAT(rcv_up);
16902529f56eSJonathan T. Looney 	COPY_STAT(rcv_adv);
16912529f56eSJonathan T. Looney 	COPY_STAT(rcv_nxt);
16922529f56eSJonathan T. Looney 	COPY_STAT(rcv_wnd);
16932529f56eSJonathan T. Looney 	COPY_STAT_T(dupacks);
16942529f56eSJonathan T. Looney 	COPY_STAT_T(segqlen);
16952529f56eSJonathan T. Looney 	COPY_STAT(snd_numholes);
16962529f56eSJonathan T. Looney 	COPY_STAT(snd_scale);
16972529f56eSJonathan T. Looney 	COPY_STAT(rcv_scale);
1698e854dd38SRandall Stewart 	COPY_STAT_T(flags2);
1699e854dd38SRandall Stewart 	COPY_STAT_T(fbyte_in);
1700e854dd38SRandall Stewart 	COPY_STAT_T(fbyte_out);
17012529f56eSJonathan T. Looney #undef COPY_STAT
17022529f56eSJonathan T. Looney #undef COPY_STAT_T
17032529f56eSJonathan T. Looney 	log_buf->tlb_flex1 = 0;
17042529f56eSJonathan T. Looney 	log_buf->tlb_flex2 = 0;
17052529f56eSJonathan T. Looney 	/* Copy stack-specific info. */
17062529f56eSJonathan T. Looney 	if (stackinfo != NULL) {
17072529f56eSJonathan T. Looney 		memcpy(&log_buf->tlb_stackinfo, stackinfo,
17082529f56eSJonathan T. Looney 		    sizeof(log_buf->tlb_stackinfo));
17092529f56eSJonathan T. Looney 		log_buf->tlb_eventflags |= TLB_FLAG_STACKINFO;
17102529f56eSJonathan T. Looney 	}
17112529f56eSJonathan T. Looney 
17122529f56eSJonathan T. Looney 	/* The packet */
17132529f56eSJonathan T. Looney 	log_buf->tlb_len = len;
17142529f56eSJonathan T. Looney 	if (th) {
17152529f56eSJonathan T. Looney 		int optlen;
17162529f56eSJonathan T. Looney 
17172529f56eSJonathan T. Looney 		log_buf->tlb_eventflags |= TLB_FLAG_HDR;
17182529f56eSJonathan T. Looney 		log_buf->tlb_th = *th;
17192529f56eSJonathan T. Looney 		if (th_hostorder)
17202529f56eSJonathan T. Looney 			tcp_fields_to_net(&log_buf->tlb_th);
17212529f56eSJonathan T. Looney 		optlen = (th->th_off << 2) - sizeof (struct tcphdr);
17222529f56eSJonathan T. Looney 		if (optlen > 0)
17232529f56eSJonathan T. Looney 			memcpy(log_buf->tlb_opts, th + 1, optlen);
17242529f56eSJonathan T. Looney 	}
17252529f56eSJonathan T. Looney 
17262529f56eSJonathan T. Looney 	/* Verbose information */
17272529f56eSJonathan T. Looney 	if (func != NULL) {
17282529f56eSJonathan T. Looney 		log_buf->tlb_eventflags |= TLB_FLAG_VERBOSE;
17292529f56eSJonathan T. Looney 		if (output_caller != NULL)
17302529f56eSJonathan T. Looney 			strlcpy(log_verbose->tlv_snd_frm, output_caller,
17312529f56eSJonathan T. Looney 			    TCP_FUNC_LEN);
17322529f56eSJonathan T. Looney 		else
17332529f56eSJonathan T. Looney 			*log_verbose->tlv_snd_frm = 0;
17342529f56eSJonathan T. Looney 		strlcpy(log_verbose->tlv_trace_func, func, TCP_FUNC_LEN);
17352529f56eSJonathan T. Looney 		log_verbose->tlv_trace_line = line;
17362529f56eSJonathan T. Looney 	}
17372529f56eSJonathan T. Looney 
17382529f56eSJonathan T. Looney 	/* Insert the new log at the tail. */
17392529f56eSJonathan T. Looney 	STAILQ_INSERT_TAIL(&tp->t_logs, log_entry, tlm_queue);
17402529f56eSJonathan T. Looney 	tcp_log_entry_refcnt_add(log_entry);
17412529f56eSJonathan T. Looney 	return (log_buf);
17422529f56eSJonathan T. Looney }
17432529f56eSJonathan T. Looney 
17442529f56eSJonathan T. Looney /*
17452529f56eSJonathan T. Looney  * Change the logging state for a TCPCB. Returns 0 on success or an
17462529f56eSJonathan T. Looney  * error code on failure.
17472529f56eSJonathan T. Looney  */
17482529f56eSJonathan T. Looney int
17492529f56eSJonathan T. Looney tcp_log_state_change(struct tcpcb *tp, int state)
17502529f56eSJonathan T. Looney {
17512529f56eSJonathan T. Looney 	struct tcp_log_mem *log_entry;
17522529f56eSJonathan T. Looney 
17539eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
17542529f56eSJonathan T. Looney 	switch(state) {
17552529f56eSJonathan T. Looney 	case TCP_LOG_STATE_CLEAR:
17562529f56eSJonathan T. Looney 		while ((log_entry = STAILQ_FIRST(&tp->t_logs)) != NULL)
17572529f56eSJonathan T. Looney 			tcp_log_remove_log_head(tp, log_entry);
17582529f56eSJonathan T. Looney 		/* Fall through */
17592529f56eSJonathan T. Looney 
17602529f56eSJonathan T. Looney 	case TCP_LOG_STATE_OFF:
17612529f56eSJonathan T. Looney 		tp->t_logstate = TCP_LOG_STATE_OFF;
17622529f56eSJonathan T. Looney 		break;
17632529f56eSJonathan T. Looney 
17642529f56eSJonathan T. Looney 	case TCP_LOG_STATE_TAIL:
17652529f56eSJonathan T. Looney 	case TCP_LOG_STATE_HEAD:
17662529f56eSJonathan T. Looney 	case TCP_LOG_STATE_CONTINUAL:
17672529f56eSJonathan T. Looney 	case TCP_LOG_STATE_HEAD_AUTO:
17682529f56eSJonathan T. Looney 	case TCP_LOG_STATE_TAIL_AUTO:
17692529f56eSJonathan T. Looney 		tp->t_logstate = state;
17702529f56eSJonathan T. Looney 		break;
17712529f56eSJonathan T. Looney 
17722529f56eSJonathan T. Looney 	default:
17732529f56eSJonathan T. Looney 		return (EINVAL);
17742529f56eSJonathan T. Looney 	}
1775a9a08eceSRandall Stewart 	if (tcp_disable_all_bb_logs) {
1776a9a08eceSRandall Stewart 		/* We are prohibited from doing any logs */
1777a9a08eceSRandall Stewart 		tp->t_logstate = TCP_LOG_STATE_OFF;
1778a9a08eceSRandall Stewart 	}
17792529f56eSJonathan T. Looney 	tp->t_flags2 &= ~(TF2_LOG_AUTO);
17802529f56eSJonathan T. Looney 
17812529f56eSJonathan T. Looney 	return (0);
17822529f56eSJonathan T. Looney }
17832529f56eSJonathan T. Looney 
17842529f56eSJonathan T. Looney /* If tcp_drain() is called, flush half the log entries. */
17852529f56eSJonathan T. Looney void
17862529f56eSJonathan T. Looney tcp_log_drain(struct tcpcb *tp)
17872529f56eSJonathan T. Looney {
17882529f56eSJonathan T. Looney 	struct tcp_log_mem *log_entry, *next;
17892529f56eSJonathan T. Looney 	int target, skip;
17902529f56eSJonathan T. Looney 
17919eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(tptoinpcb(tp));
17922529f56eSJonathan T. Looney 	if ((target = tp->t_lognum / 2) == 0)
17932529f56eSJonathan T. Looney 		return;
17942529f56eSJonathan T. Looney 
17952529f56eSJonathan T. Looney 	/*
17962529f56eSJonathan T. Looney 	 * If we are logging the "head" packets, we want to discard
17972529f56eSJonathan T. Looney 	 * from the tail of the queue. Otherwise, we want to discard
17982529f56eSJonathan T. Looney 	 * from the head.
17992529f56eSJonathan T. Looney 	 */
18002529f56eSJonathan T. Looney 	if (tp->t_logstate == TCP_LOG_STATE_HEAD ||
18012529f56eSJonathan T. Looney 	    tp->t_logstate == TCP_LOG_STATE_HEAD_AUTO) {
18022529f56eSJonathan T. Looney 		skip = tp->t_lognum - target;
18032529f56eSJonathan T. Looney 		STAILQ_FOREACH(log_entry, &tp->t_logs, tlm_queue)
18042529f56eSJonathan T. Looney 			if (!--skip)
18052529f56eSJonathan T. Looney 				break;
18062529f56eSJonathan T. Looney 		KASSERT(log_entry != NULL,
18072529f56eSJonathan T. Looney 		    ("%s: skipped through all entries!", __func__));
18082529f56eSJonathan T. Looney 		if (log_entry == NULL)
18092529f56eSJonathan T. Looney 			return;
18102529f56eSJonathan T. Looney 		while ((next = STAILQ_NEXT(log_entry, tlm_queue)) != NULL) {
18112529f56eSJonathan T. Looney 			STAILQ_REMOVE_AFTER(&tp->t_logs, log_entry, tlm_queue);
18122529f56eSJonathan T. Looney 			tcp_log_entry_refcnt_rem(next);
18132529f56eSJonathan T. Looney 			tcp_log_remove_log_cleanup(tp, next);
18142529f56eSJonathan T. Looney #ifdef INVARIANTS
18152529f56eSJonathan T. Looney 			target--;
18162529f56eSJonathan T. Looney #endif
18172529f56eSJonathan T. Looney 		}
18182529f56eSJonathan T. Looney 		KASSERT(target == 0,
18192529f56eSJonathan T. Looney 		    ("%s: After removing from tail, target was %d", __func__,
18202529f56eSJonathan T. Looney 			target));
18212529f56eSJonathan T. Looney 	} else if (tp->t_logstate == TCP_LOG_STATE_CONTINUAL) {
18222529f56eSJonathan T. Looney 		(void)tcp_log_dump_tp_logbuf(tp, "auto-dumped from continual",
18232529f56eSJonathan T. Looney 		    M_NOWAIT, false);
18242529f56eSJonathan T. Looney 	} else {
18252529f56eSJonathan T. Looney 		while ((log_entry = STAILQ_FIRST(&tp->t_logs)) != NULL &&
18262529f56eSJonathan T. Looney 		    target--)
18272529f56eSJonathan T. Looney 			tcp_log_remove_log_head(tp, log_entry);
18282529f56eSJonathan T. Looney 		KASSERT(target <= 0,
18292529f56eSJonathan T. Looney 		    ("%s: After removing from head, target was %d", __func__,
18302529f56eSJonathan T. Looney 			target));
18312529f56eSJonathan T. Looney 		KASSERT(tp->t_lognum > 0,
18322529f56eSJonathan T. Looney 		    ("%s: After removing from head, tp->t_lognum was %d",
18332529f56eSJonathan T. Looney 			__func__, target));
18342529f56eSJonathan T. Looney 		KASSERT(log_entry != NULL,
18352529f56eSJonathan T. Looney 		    ("%s: After removing from head, the tailq was empty",
18362529f56eSJonathan T. Looney 			__func__));
18372529f56eSJonathan T. Looney 	}
18382529f56eSJonathan T. Looney }
18392529f56eSJonathan T. Looney 
18402529f56eSJonathan T. Looney static inline int
18412529f56eSJonathan T. Looney tcp_log_copyout(struct sockopt *sopt, void *src, void *dst, size_t len)
18422529f56eSJonathan T. Looney {
18432529f56eSJonathan T. Looney 
18442529f56eSJonathan T. Looney 	if (sopt->sopt_td != NULL)
18452529f56eSJonathan T. Looney 		return (copyout(src, dst, len));
18462529f56eSJonathan T. Looney 	bcopy(src, dst, len);
18472529f56eSJonathan T. Looney 	return (0);
18482529f56eSJonathan T. Looney }
18492529f56eSJonathan T. Looney 
18502529f56eSJonathan T. Looney static int
18512529f56eSJonathan T. Looney tcp_log_logs_to_buf(struct sockopt *sopt, struct tcp_log_stailq *log_tailqp,
18522529f56eSJonathan T. Looney     struct tcp_log_buffer **end, int count)
18532529f56eSJonathan T. Looney {
18542529f56eSJonathan T. Looney 	struct tcp_log_buffer *out_entry;
18552529f56eSJonathan T. Looney 	struct tcp_log_mem *log_entry;
18562529f56eSJonathan T. Looney 	size_t entrysize;
18572529f56eSJonathan T. Looney 	int error;
18582529f56eSJonathan T. Looney #ifdef INVARIANTS
18592529f56eSJonathan T. Looney 	int orig_count = count;
18602529f56eSJonathan T. Looney #endif
18612529f56eSJonathan T. Looney 
18622529f56eSJonathan T. Looney 	/* Copy the data out. */
18632529f56eSJonathan T. Looney 	error = 0;
18642529f56eSJonathan T. Looney 	out_entry = (struct tcp_log_buffer *) sopt->sopt_val;
18652529f56eSJonathan T. Looney 	STAILQ_FOREACH(log_entry, log_tailqp, tlm_queue) {
18662529f56eSJonathan T. Looney 		count--;
18672529f56eSJonathan T. Looney 		KASSERT(count >= 0,
18682529f56eSJonathan T. Looney 		    ("%s:%d: Exceeded expected count (%d) processing list %p",
18692529f56eSJonathan T. Looney 		    __func__, __LINE__, orig_count, log_tailqp));
18702529f56eSJonathan T. Looney 
18712529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
18722529f56eSJonathan T. Looney 		counter_u64_add(tcp_log_que_copyout, 1);
18732529f56eSJonathan T. Looney #endif
18742529f56eSJonathan T. Looney 
18752529f56eSJonathan T. Looney 		/*
18762529f56eSJonathan T. Looney 		 * Skip copying out the header if it isn't present.
18772529f56eSJonathan T. Looney 		 * Instead, copy out zeros (to ensure we don't leak info).
18782529f56eSJonathan T. Looney 		 * TODO: Make sure we truly do zero everything we don't
18792529f56eSJonathan T. Looney 		 * explicitly set.
18802529f56eSJonathan T. Looney 		 */
18812529f56eSJonathan T. Looney 		if (log_entry->tlm_buf.tlb_eventflags & TLB_FLAG_HDR)
18822529f56eSJonathan T. Looney 			entrysize = sizeof(struct tcp_log_buffer);
18832529f56eSJonathan T. Looney 		else
18842529f56eSJonathan T. Looney 			entrysize = offsetof(struct tcp_log_buffer, tlb_th);
18852529f56eSJonathan T. Looney 		error = tcp_log_copyout(sopt, &log_entry->tlm_buf, out_entry,
18862529f56eSJonathan T. Looney 		    entrysize);
18872529f56eSJonathan T. Looney 		if (error)
18882529f56eSJonathan T. Looney 			break;
18892529f56eSJonathan T. Looney 		if (!(log_entry->tlm_buf.tlb_eventflags & TLB_FLAG_HDR)) {
18902529f56eSJonathan T. Looney 			error = tcp_log_copyout(sopt, zerobuf,
18912529f56eSJonathan T. Looney 			    ((uint8_t *)out_entry) + entrysize,
18922529f56eSJonathan T. Looney 			    sizeof(struct tcp_log_buffer) - entrysize);
18932529f56eSJonathan T. Looney 		}
18942529f56eSJonathan T. Looney 
18952529f56eSJonathan T. Looney 		/*
18962529f56eSJonathan T. Looney 		 * Copy out the verbose bit, if needed. Either way,
18972529f56eSJonathan T. Looney 		 * increment the output pointer the correct amount.
18982529f56eSJonathan T. Looney 		 */
18992529f56eSJonathan T. Looney 		if (log_entry->tlm_buf.tlb_eventflags & TLB_FLAG_VERBOSE) {
19002529f56eSJonathan T. Looney 			error = tcp_log_copyout(sopt, &log_entry->tlm_v,
19012529f56eSJonathan T. Looney 			    out_entry->tlb_verbose,
19022529f56eSJonathan T. Looney 			    sizeof(struct tcp_log_verbose));
19032529f56eSJonathan T. Looney 			if (error)
19042529f56eSJonathan T. Looney 				break;
19052529f56eSJonathan T. Looney 			out_entry = (struct tcp_log_buffer *)
19062529f56eSJonathan T. Looney 			    (((uint8_t *) (out_entry + 1)) +
19072529f56eSJonathan T. Looney 			    sizeof(struct tcp_log_verbose));
19082529f56eSJonathan T. Looney 		} else
19092529f56eSJonathan T. Looney 			out_entry++;
19102529f56eSJonathan T. Looney 	}
19112529f56eSJonathan T. Looney 	*end = out_entry;
19122529f56eSJonathan T. Looney 	KASSERT(error || count == 0,
19132529f56eSJonathan T. Looney 	    ("%s:%d: Less than expected count (%d) processing list %p"
19142529f56eSJonathan T. Looney 	    " (%d remain)", __func__, __LINE__, orig_count,
19152529f56eSJonathan T. Looney 	    log_tailqp, count));
19162529f56eSJonathan T. Looney 
19172529f56eSJonathan T. Looney 	return (error);
19182529f56eSJonathan T. Looney }
19192529f56eSJonathan T. Looney 
19202529f56eSJonathan T. Looney /*
19212529f56eSJonathan T. Looney  * Copy out the buffer. Note that we do incremental copying, so
19222529f56eSJonathan T. Looney  * sooptcopyout() won't work. However, the goal is to produce the same
19232529f56eSJonathan T. Looney  * end result as if we copied in the entire user buffer, updated it,
19242529f56eSJonathan T. Looney  * and then used sooptcopyout() to copy it out.
19252529f56eSJonathan T. Looney  *
19262529f56eSJonathan T. Looney  * NOTE: This should be called with a write lock on the PCB; however,
19272529f56eSJonathan T. Looney  * the function will drop it after it extracts the data from the TCPCB.
19282529f56eSJonathan T. Looney  */
19292529f56eSJonathan T. Looney int
19302529f56eSJonathan T. Looney tcp_log_getlogbuf(struct sockopt *sopt, struct tcpcb *tp)
19312529f56eSJonathan T. Looney {
19322529f56eSJonathan T. Looney 	struct tcp_log_stailq log_tailq;
19332529f56eSJonathan T. Looney 	struct tcp_log_mem *log_entry, *log_next;
19342529f56eSJonathan T. Looney 	struct tcp_log_buffer *out_entry;
19359eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
19362529f56eSJonathan T. Looney 	size_t outsize, entrysize;
19372529f56eSJonathan T. Looney 	int error, outnum;
19382529f56eSJonathan T. Looney 
19399eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(inp);
19402529f56eSJonathan T. Looney 
19412529f56eSJonathan T. Looney 	/*
19422529f56eSJonathan T. Looney 	 * Determine which log entries will fit in the buffer. As an
19432529f56eSJonathan T. Looney 	 * optimization, skip this if all the entries will clearly fit
19442529f56eSJonathan T. Looney 	 * in the buffer. (However, get an exact size if we are using
19452529f56eSJonathan T. Looney 	 * INVARIANTS.)
19462529f56eSJonathan T. Looney 	 */
19472529f56eSJonathan T. Looney #ifndef INVARIANTS
19482529f56eSJonathan T. Looney 	if (sopt->sopt_valsize / (sizeof(struct tcp_log_buffer) +
19492529f56eSJonathan T. Looney 	    sizeof(struct tcp_log_verbose)) >= tp->t_lognum) {
19502529f56eSJonathan T. Looney 		log_entry = STAILQ_LAST(&tp->t_logs, tcp_log_mem, tlm_queue);
19512529f56eSJonathan T. Looney 		log_next = NULL;
19522529f56eSJonathan T. Looney 		outsize = 0;
19532529f56eSJonathan T. Looney 		outnum = tp->t_lognum;
19542529f56eSJonathan T. Looney 	} else {
19552529f56eSJonathan T. Looney #endif
19562529f56eSJonathan T. Looney 		outsize = outnum = 0;
19572529f56eSJonathan T. Looney 		log_entry = NULL;
19582529f56eSJonathan T. Looney 		STAILQ_FOREACH(log_next, &tp->t_logs, tlm_queue) {
19592529f56eSJonathan T. Looney 			entrysize = sizeof(struct tcp_log_buffer);
19602529f56eSJonathan T. Looney 			if (log_next->tlm_buf.tlb_eventflags &
19612529f56eSJonathan T. Looney 			    TLB_FLAG_VERBOSE)
19622529f56eSJonathan T. Looney 				entrysize += sizeof(struct tcp_log_verbose);
19632529f56eSJonathan T. Looney 			if ((sopt->sopt_valsize - outsize) < entrysize)
19642529f56eSJonathan T. Looney 				break;
19652529f56eSJonathan T. Looney 			outsize += entrysize;
19662529f56eSJonathan T. Looney 			outnum++;
19672529f56eSJonathan T. Looney 			log_entry = log_next;
19682529f56eSJonathan T. Looney 		}
19692529f56eSJonathan T. Looney 		KASSERT(outsize <= sopt->sopt_valsize,
19702529f56eSJonathan T. Looney 		    ("%s: calculated output size (%zu) greater than available"
19712529f56eSJonathan T. Looney 			"space (%zu)", __func__, outsize, sopt->sopt_valsize));
19722529f56eSJonathan T. Looney #ifndef INVARIANTS
19732529f56eSJonathan T. Looney 	}
19742529f56eSJonathan T. Looney #endif
19752529f56eSJonathan T. Looney 
19762529f56eSJonathan T. Looney 	/*
19772529f56eSJonathan T. Looney 	 * Copy traditional sooptcopyout() behavior: if sopt->sopt_val
19782529f56eSJonathan T. Looney 	 * is NULL, silently skip the copy. However, in this case, we
19792529f56eSJonathan T. Looney 	 * will leave the list alone and return. Functionally, this
19802529f56eSJonathan T. Looney 	 * gives userspace a way to poll for an approximate buffer
19812529f56eSJonathan T. Looney 	 * size they will need to get the log entries.
19822529f56eSJonathan T. Looney 	 */
19832529f56eSJonathan T. Looney 	if (sopt->sopt_val == NULL) {
19842529f56eSJonathan T. Looney 		INP_WUNLOCK(inp);
19852529f56eSJonathan T. Looney 		if (outsize == 0) {
19862529f56eSJonathan T. Looney 			outsize = outnum * (sizeof(struct tcp_log_buffer) +
19872529f56eSJonathan T. Looney 			    sizeof(struct tcp_log_verbose));
19882529f56eSJonathan T. Looney 		}
19892529f56eSJonathan T. Looney 		if (sopt->sopt_valsize > outsize)
19902529f56eSJonathan T. Looney 			sopt->sopt_valsize = outsize;
19912529f56eSJonathan T. Looney 		return (0);
19922529f56eSJonathan T. Looney 	}
19932529f56eSJonathan T. Looney 
19942529f56eSJonathan T. Looney 	/*
19952529f56eSJonathan T. Looney 	 * Break apart the list. We'll save the ones we want to copy
19962529f56eSJonathan T. Looney 	 * out locally and remove them from the TCPCB list. We can
19972529f56eSJonathan T. Looney 	 * then drop the INPCB lock while we do the copyout.
19982529f56eSJonathan T. Looney 	 *
19992529f56eSJonathan T. Looney 	 * There are roughly three cases:
20002529f56eSJonathan T. Looney 	 * 1. There was nothing to copy out. That's easy: drop the
20012529f56eSJonathan T. Looney 	 * lock and return.
20022529f56eSJonathan T. Looney 	 * 2. We are copying out the entire list. Again, that's easy:
20032529f56eSJonathan T. Looney 	 * move the whole list.
20042529f56eSJonathan T. Looney 	 * 3. We are copying out a partial list. That's harder. We
20052529f56eSJonathan T. Looney 	 * need to update the list book-keeping entries.
20062529f56eSJonathan T. Looney 	 */
20072529f56eSJonathan T. Looney 	if (log_entry != NULL && log_next == NULL) {
20082529f56eSJonathan T. Looney 		/* Move entire list. */
20092529f56eSJonathan T. Looney 		KASSERT(outnum == tp->t_lognum,
20102529f56eSJonathan T. Looney 		    ("%s:%d: outnum (%d) should match tp->t_lognum (%d)",
20112529f56eSJonathan T. Looney 			__func__, __LINE__, outnum, tp->t_lognum));
20122529f56eSJonathan T. Looney 		log_tailq = tp->t_logs;
20132529f56eSJonathan T. Looney 		tp->t_lognum = 0;
20142529f56eSJonathan T. Looney 		STAILQ_INIT(&tp->t_logs);
20152529f56eSJonathan T. Looney 	} else if (log_entry != NULL) {
20162529f56eSJonathan T. Looney 		/* Move partial list. */
20172529f56eSJonathan T. Looney 		KASSERT(outnum < tp->t_lognum,
20182529f56eSJonathan T. Looney 		    ("%s:%d: outnum (%d) not less than tp->t_lognum (%d)",
20192529f56eSJonathan T. Looney 			__func__, __LINE__, outnum, tp->t_lognum));
20202529f56eSJonathan T. Looney 		STAILQ_FIRST(&log_tailq) = STAILQ_FIRST(&tp->t_logs);
20212529f56eSJonathan T. Looney 		STAILQ_FIRST(&tp->t_logs) = STAILQ_NEXT(log_entry, tlm_queue);
20222529f56eSJonathan T. Looney 		KASSERT(STAILQ_NEXT(log_entry, tlm_queue) != NULL,
20232529f56eSJonathan T. Looney 		    ("%s:%d: tp->t_logs is unexpectedly shorter than expected"
20242529f56eSJonathan T. Looney 		    "(tp: %p, log_tailq: %p, outnum: %d, tp->t_lognum: %d)",
20252529f56eSJonathan T. Looney 		    __func__, __LINE__, tp, &log_tailq, outnum, tp->t_lognum));
20262529f56eSJonathan T. Looney 		STAILQ_NEXT(log_entry, tlm_queue) = NULL;
20272529f56eSJonathan T. Looney 		log_tailq.stqh_last = &STAILQ_NEXT(log_entry, tlm_queue);
20282529f56eSJonathan T. Looney 		tp->t_lognum -= outnum;
20292529f56eSJonathan T. Looney 	} else
20302529f56eSJonathan T. Looney 		STAILQ_INIT(&log_tailq);
20312529f56eSJonathan T. Looney 
20322529f56eSJonathan T. Looney 	/* Drop the PCB lock. */
20332529f56eSJonathan T. Looney 	INP_WUNLOCK(inp);
20342529f56eSJonathan T. Looney 
20352529f56eSJonathan T. Looney 	/* Copy the data out. */
20362529f56eSJonathan T. Looney 	error = tcp_log_logs_to_buf(sopt, &log_tailq, &out_entry, outnum);
20372529f56eSJonathan T. Looney 
20382529f56eSJonathan T. Looney 	if (error) {
20392529f56eSJonathan T. Looney 		/* Restore list */
20402529f56eSJonathan T. Looney 		INP_WLOCK(inp);
204153af6903SGleb Smirnoff 		if ((inp->inp_flags & INP_DROPPED) == 0) {
20422529f56eSJonathan T. Looney 			tp = intotcpcb(inp);
20432529f56eSJonathan T. Looney 
20442529f56eSJonathan T. Looney 			/* Merge the two lists. */
20452529f56eSJonathan T. Looney 			STAILQ_CONCAT(&log_tailq, &tp->t_logs);
20462529f56eSJonathan T. Looney 			tp->t_logs = log_tailq;
20472529f56eSJonathan T. Looney 			tp->t_lognum += outnum;
20482529f56eSJonathan T. Looney 		}
20492529f56eSJonathan T. Looney 		INP_WUNLOCK(inp);
20502529f56eSJonathan T. Looney 	} else {
20512529f56eSJonathan T. Looney 		/* Sanity check entries */
20522529f56eSJonathan T. Looney 		KASSERT(((caddr_t)out_entry - (caddr_t)sopt->sopt_val)  ==
20532529f56eSJonathan T. Looney 		    outsize, ("%s: Actual output size (%zu) != "
20542529f56eSJonathan T. Looney 			"calculated output size (%zu)", __func__,
20552529f56eSJonathan T. Looney 			(size_t)((caddr_t)out_entry - (caddr_t)sopt->sopt_val),
20562529f56eSJonathan T. Looney 			outsize));
20572529f56eSJonathan T. Looney 
20582529f56eSJonathan T. Looney 		/* Free the entries we just copied out. */
20592529f56eSJonathan T. Looney 		STAILQ_FOREACH_SAFE(log_entry, &log_tailq, tlm_queue, log_next) {
20602529f56eSJonathan T. Looney 			tcp_log_entry_refcnt_rem(log_entry);
20612529f56eSJonathan T. Looney 			uma_zfree(tcp_log_zone, log_entry);
20622529f56eSJonathan T. Looney 		}
20632529f56eSJonathan T. Looney 	}
20642529f56eSJonathan T. Looney 
20652529f56eSJonathan T. Looney 	sopt->sopt_valsize = (size_t)((caddr_t)out_entry -
20662529f56eSJonathan T. Looney 	    (caddr_t)sopt->sopt_val);
20672529f56eSJonathan T. Looney 	return (error);
20682529f56eSJonathan T. Looney }
20692529f56eSJonathan T. Looney 
20702529f56eSJonathan T. Looney static void
20712529f56eSJonathan T. Looney tcp_log_free_queue(struct tcp_log_dev_queue *param)
20722529f56eSJonathan T. Looney {
20732529f56eSJonathan T. Looney 	struct tcp_log_dev_log_queue *entry;
20742529f56eSJonathan T. Looney 
20752529f56eSJonathan T. Looney 	KASSERT(param != NULL, ("%s: called with NULL param", __func__));
20762529f56eSJonathan T. Looney 	if (param == NULL)
20772529f56eSJonathan T. Looney 		return;
20782529f56eSJonathan T. Looney 
20792529f56eSJonathan T. Looney 	entry = (struct tcp_log_dev_log_queue *)param;
20802529f56eSJonathan T. Looney 
20812529f56eSJonathan T. Looney 	/* Free the entries. */
20822529f56eSJonathan T. Looney 	tcp_log_free_entries(&entry->tldl_entries, &entry->tldl_count);
20832529f56eSJonathan T. Looney 
20842529f56eSJonathan T. Looney 	/* Free the buffer, if it is allocated. */
20852529f56eSJonathan T. Looney 	if (entry->tldl_common.tldq_buf != NULL)
20862529f56eSJonathan T. Looney 		free(entry->tldl_common.tldq_buf, M_TCPLOGDEV);
20872529f56eSJonathan T. Looney 
20882529f56eSJonathan T. Looney 	/* Free the queue entry. */
20892529f56eSJonathan T. Looney 	free(entry, M_TCPLOGDEV);
20902529f56eSJonathan T. Looney }
20912529f56eSJonathan T. Looney 
20922529f56eSJonathan T. Looney static struct tcp_log_common_header *
20932529f56eSJonathan T. Looney tcp_log_expandlogbuf(struct tcp_log_dev_queue *param)
20942529f56eSJonathan T. Looney {
20952529f56eSJonathan T. Looney 	struct tcp_log_dev_log_queue *entry;
20962529f56eSJonathan T. Looney 	struct tcp_log_header *hdr;
20972529f56eSJonathan T. Looney 	uint8_t *end;
20982529f56eSJonathan T. Looney 	struct sockopt sopt;
20992529f56eSJonathan T. Looney 	int error;
21002529f56eSJonathan T. Looney 
21012529f56eSJonathan T. Looney 	entry = (struct tcp_log_dev_log_queue *)param;
21022529f56eSJonathan T. Looney 
21032529f56eSJonathan T. Looney 	/* Take a worst-case guess at space needs. */
21042529f56eSJonathan T. Looney 	sopt.sopt_valsize = sizeof(struct tcp_log_header) +
21052529f56eSJonathan T. Looney 	    entry->tldl_count * (sizeof(struct tcp_log_buffer) +
21062529f56eSJonathan T. Looney 	    sizeof(struct tcp_log_verbose));
21072529f56eSJonathan T. Looney 	hdr = malloc(sopt.sopt_valsize, M_TCPLOGDEV, M_NOWAIT);
21082529f56eSJonathan T. Looney 	if (hdr == NULL) {
21092529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
21102529f56eSJonathan T. Looney 		counter_u64_add(tcp_log_que_fail5, entry->tldl_count);
21112529f56eSJonathan T. Looney #endif
21122529f56eSJonathan T. Looney 		return (NULL);
21132529f56eSJonathan T. Looney 	}
21142529f56eSJonathan T. Looney 	sopt.sopt_val = hdr + 1;
21152529f56eSJonathan T. Looney 	sopt.sopt_valsize -= sizeof(struct tcp_log_header);
21162529f56eSJonathan T. Looney 	sopt.sopt_td = NULL;
21172529f56eSJonathan T. Looney 
21182529f56eSJonathan T. Looney 	error = tcp_log_logs_to_buf(&sopt, &entry->tldl_entries,
21192529f56eSJonathan T. Looney 	    (struct tcp_log_buffer **)&end, entry->tldl_count);
21202529f56eSJonathan T. Looney 	if (error) {
21212529f56eSJonathan T. Looney 		free(hdr, M_TCPLOGDEV);
21222529f56eSJonathan T. Looney 		return (NULL);
21232529f56eSJonathan T. Looney 	}
21242529f56eSJonathan T. Looney 
21252529f56eSJonathan T. Looney 	/* Free the entries. */
21262529f56eSJonathan T. Looney 	tcp_log_free_entries(&entry->tldl_entries, &entry->tldl_count);
21272529f56eSJonathan T. Looney 	entry->tldl_count = 0;
21282529f56eSJonathan T. Looney 
21292529f56eSJonathan T. Looney 	memset(hdr, 0, sizeof(struct tcp_log_header));
21302529f56eSJonathan T. Looney 	hdr->tlh_version = TCP_LOG_BUF_VER;
21312529f56eSJonathan T. Looney 	hdr->tlh_type = TCP_LOG_DEV_TYPE_BBR;
21322529f56eSJonathan T. Looney 	hdr->tlh_length = end - (uint8_t *)hdr;
21332529f56eSJonathan T. Looney 	hdr->tlh_ie = entry->tldl_ie;
21342529f56eSJonathan T. Looney 	hdr->tlh_af = entry->tldl_af;
21352529f56eSJonathan T. Looney 	getboottime(&hdr->tlh_offset);
21362529f56eSJonathan T. Looney 	strlcpy(hdr->tlh_id, entry->tldl_id, TCP_LOG_ID_LEN);
2137a9a08eceSRandall Stewart 	strlcpy(hdr->tlh_tag, entry->tldl_tag, TCP_LOG_TAG_LEN);
21382529f56eSJonathan T. Looney 	strlcpy(hdr->tlh_reason, entry->tldl_reason, TCP_LOG_REASON_LEN);
21392529f56eSJonathan T. Looney 	return ((struct tcp_log_common_header *)hdr);
21402529f56eSJonathan T. Looney }
21412529f56eSJonathan T. Looney 
21422529f56eSJonathan T. Looney /*
21432529f56eSJonathan T. Looney  * Queue the tcpcb's log buffer for transmission via the log buffer facility.
21442529f56eSJonathan T. Looney  *
21452529f56eSJonathan T. Looney  * NOTE: This should be called with a write lock on the PCB.
21462529f56eSJonathan T. Looney  *
21472529f56eSJonathan T. Looney  * how should be M_WAITOK or M_NOWAIT. If M_WAITOK, the function will drop
21482529f56eSJonathan T. Looney  * and reacquire the INP lock if it needs to do so.
21492529f56eSJonathan T. Looney  *
21502529f56eSJonathan T. Looney  * If force is false, this will only dump auto-logged sessions if
21512529f56eSJonathan T. Looney  * tcp_log_auto_all is true or if there is a log ID defined for the session.
21522529f56eSJonathan T. Looney  */
21532529f56eSJonathan T. Looney int
21542529f56eSJonathan T. Looney tcp_log_dump_tp_logbuf(struct tcpcb *tp, char *reason, int how, bool force)
21552529f56eSJonathan T. Looney {
21562529f56eSJonathan T. Looney 	struct tcp_log_dev_log_queue *entry;
21579eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
21582529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
21592529f56eSJonathan T. Looney 	int num_entries;
21602529f56eSJonathan T. Looney #endif
21612529f56eSJonathan T. Looney 
21622529f56eSJonathan T. Looney 	INP_WLOCK_ASSERT(inp);
21632529f56eSJonathan T. Looney 
21642529f56eSJonathan T. Looney 	/* If there are no log entries, there is nothing to do. */
21652529f56eSJonathan T. Looney 	if (tp->t_lognum == 0)
21662529f56eSJonathan T. Looney 		return (0);
21672529f56eSJonathan T. Looney 
21682529f56eSJonathan T. Looney 	/* Check for a log ID. */
21692529f56eSJonathan T. Looney 	if (tp->t_lib == NULL && (tp->t_flags2 & TF2_LOG_AUTO) &&
21702529f56eSJonathan T. Looney 	    !tcp_log_auto_all && !force) {
21712529f56eSJonathan T. Looney 		struct tcp_log_mem *log_entry;
21722529f56eSJonathan T. Looney 
21732529f56eSJonathan T. Looney 		/*
21742529f56eSJonathan T. Looney 		 * We needed a log ID and none was found. Free the log entries
21752529f56eSJonathan T. Looney 		 * and return success. Also, cancel further logging. If the
21762529f56eSJonathan T. Looney 		 * session doesn't have a log ID by now, we'll assume it isn't
21772529f56eSJonathan T. Looney 		 * going to get one.
21782529f56eSJonathan T. Looney 		 */
21792529f56eSJonathan T. Looney 		while ((log_entry = STAILQ_FIRST(&tp->t_logs)) != NULL)
21802529f56eSJonathan T. Looney 			tcp_log_remove_log_head(tp, log_entry);
21812529f56eSJonathan T. Looney 		KASSERT(tp->t_lognum == 0,
21822529f56eSJonathan T. Looney 		    ("%s: After freeing entries, tp->t_lognum=%d (expected 0)",
21832529f56eSJonathan T. Looney 			__func__, tp->t_lognum));
21842529f56eSJonathan T. Looney 		tp->t_logstate = TCP_LOG_STATE_OFF;
21852529f56eSJonathan T. Looney 		return (0);
21862529f56eSJonathan T. Looney 	}
21872529f56eSJonathan T. Looney 
21882529f56eSJonathan T. Looney 	/*
21892529f56eSJonathan T. Looney 	 * Allocate memory. If we must wait, we'll need to drop the locks
21902529f56eSJonathan T. Looney 	 * and reacquire them (and do all the related business that goes
21912529f56eSJonathan T. Looney 	 * along with that).
21922529f56eSJonathan T. Looney 	 */
21932529f56eSJonathan T. Looney 	entry = malloc(sizeof(struct tcp_log_dev_log_queue), M_TCPLOGDEV,
21942529f56eSJonathan T. Looney 	    M_NOWAIT);
21952529f56eSJonathan T. Looney 	if (entry == NULL && (how & M_NOWAIT)) {
21962529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
21972529f56eSJonathan T. Looney 		counter_u64_add(tcp_log_que_fail3, 1);
21982529f56eSJonathan T. Looney #endif
21992529f56eSJonathan T. Looney 		return (ENOBUFS);
22002529f56eSJonathan T. Looney 	}
22012529f56eSJonathan T. Looney 	if (entry == NULL) {
22022529f56eSJonathan T. Looney 		INP_WUNLOCK(inp);
22032529f56eSJonathan T. Looney 		entry = malloc(sizeof(struct tcp_log_dev_log_queue),
22042529f56eSJonathan T. Looney 		    M_TCPLOGDEV, M_WAITOK);
22052529f56eSJonathan T. Looney 		INP_WLOCK(inp);
22062529f56eSJonathan T. Looney 		/*
22072529f56eSJonathan T. Looney 		 * Note that this check is slightly overly-restrictive in
22082529f56eSJonathan T. Looney 		 * that the TCB can survive either of these events.
22092529f56eSJonathan T. Looney 		 * However, there is currently not a good way to ensure
22102529f56eSJonathan T. Looney 		 * that is the case. So, if we hit this M_WAIT path, we
22112529f56eSJonathan T. Looney 		 * may end up dropping some entries. That seems like a
22122529f56eSJonathan T. Looney 		 * small price to pay for safety.
22132529f56eSJonathan T. Looney 		 */
221453af6903SGleb Smirnoff 		if (inp->inp_flags & INP_DROPPED) {
22152529f56eSJonathan T. Looney 			free(entry, M_TCPLOGDEV);
22162529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
22172529f56eSJonathan T. Looney 			counter_u64_add(tcp_log_que_fail2, 1);
22182529f56eSJonathan T. Looney #endif
22192529f56eSJonathan T. Looney 			return (ECONNRESET);
22202529f56eSJonathan T. Looney 		}
22212529f56eSJonathan T. Looney 		tp = intotcpcb(inp);
22222529f56eSJonathan T. Looney 		if (tp->t_lognum == 0) {
22232529f56eSJonathan T. Looney 			free(entry, M_TCPLOGDEV);
22242529f56eSJonathan T. Looney 			return (0);
22252529f56eSJonathan T. Looney 		}
22262529f56eSJonathan T. Looney 	}
22272529f56eSJonathan T. Looney 
22282529f56eSJonathan T. Looney 	/* Fill in the unique parts of the queue entry. */
2229a9a08eceSRandall Stewart 	if (tp->t_lib != NULL) {
22302529f56eSJonathan T. Looney 		strlcpy(entry->tldl_id, tp->t_lib->tlb_id, TCP_LOG_ID_LEN);
2231a9a08eceSRandall Stewart 		strlcpy(entry->tldl_tag, tp->t_lib->tlb_tag, TCP_LOG_TAG_LEN);
2232a9a08eceSRandall Stewart 	} else {
22332529f56eSJonathan T. Looney 		strlcpy(entry->tldl_id, "UNKNOWN", TCP_LOG_ID_LEN);
2234a9a08eceSRandall Stewart 		strlcpy(entry->tldl_tag, "UNKNOWN", TCP_LOG_TAG_LEN);
2235a9a08eceSRandall Stewart 	}
22362529f56eSJonathan T. Looney 	if (reason != NULL)
22372529f56eSJonathan T. Looney 		strlcpy(entry->tldl_reason, reason, TCP_LOG_REASON_LEN);
22382529f56eSJonathan T. Looney 	else
22392529f56eSJonathan T. Looney 		strlcpy(entry->tldl_reason, "UNKNOWN", TCP_LOG_ID_LEN);
22402529f56eSJonathan T. Looney 	entry->tldl_ie = inp->inp_inc.inc_ie;
22412529f56eSJonathan T. Looney 	if (inp->inp_inc.inc_flags & INC_ISIPV6)
22422529f56eSJonathan T. Looney 		entry->tldl_af = AF_INET6;
22432529f56eSJonathan T. Looney 	else
22442529f56eSJonathan T. Looney 		entry->tldl_af = AF_INET;
22452529f56eSJonathan T. Looney 	entry->tldl_entries = tp->t_logs;
22462529f56eSJonathan T. Looney 	entry->tldl_count = tp->t_lognum;
22472529f56eSJonathan T. Looney 
22482529f56eSJonathan T. Looney 	/* Fill in the common parts of the queue entry. */
22492529f56eSJonathan T. Looney 	entry->tldl_common.tldq_buf = NULL;
22502529f56eSJonathan T. Looney 	entry->tldl_common.tldq_xform = tcp_log_expandlogbuf;
22512529f56eSJonathan T. Looney 	entry->tldl_common.tldq_dtor = tcp_log_free_queue;
22522529f56eSJonathan T. Looney 
22532529f56eSJonathan T. Looney 	/* Clear the log data from the TCPCB. */
22542529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
22552529f56eSJonathan T. Looney 	num_entries = tp->t_lognum;
22562529f56eSJonathan T. Looney #endif
22572529f56eSJonathan T. Looney 	tp->t_lognum = 0;
22582529f56eSJonathan T. Looney 	STAILQ_INIT(&tp->t_logs);
22592529f56eSJonathan T. Looney 
22602529f56eSJonathan T. Looney 	/* Add the entry. If no one is listening, free the entry. */
22612529f56eSJonathan T. Looney 	if (tcp_log_dev_add_log((struct tcp_log_dev_queue *)entry)) {
22622529f56eSJonathan T. Looney 		tcp_log_free_queue((struct tcp_log_dev_queue *)entry);
22632529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
22642529f56eSJonathan T. Looney 		counter_u64_add(tcp_log_que_fail1, num_entries);
22652529f56eSJonathan T. Looney 	} else {
22662529f56eSJonathan T. Looney 		counter_u64_add(tcp_log_queued, num_entries);
22672529f56eSJonathan T. Looney #endif
22682529f56eSJonathan T. Looney 	}
22692529f56eSJonathan T. Looney 	return (0);
22702529f56eSJonathan T. Looney }
22712529f56eSJonathan T. Looney 
22722529f56eSJonathan T. Looney /*
22732529f56eSJonathan T. Looney  * Queue the log_id_node's log buffers for transmission via the log buffer
22742529f56eSJonathan T. Looney  * facility.
22752529f56eSJonathan T. Looney  *
22762529f56eSJonathan T. Looney  * NOTE: This should be called with the bucket locked and referenced.
22772529f56eSJonathan T. Looney  *
22782529f56eSJonathan T. Looney  * how should be M_WAITOK or M_NOWAIT. If M_WAITOK, the function will drop
22792529f56eSJonathan T. Looney  * and reacquire the bucket lock if it needs to do so. (The caller must
22802529f56eSJonathan T. Looney  * ensure that the tln is no longer on any lists so no one else will mess
22812529f56eSJonathan T. Looney  * with this while the lock is dropped!)
22822529f56eSJonathan T. Looney  */
22832529f56eSJonathan T. Looney static int
22842529f56eSJonathan T. Looney tcp_log_dump_node_logbuf(struct tcp_log_id_node *tln, char *reason, int how)
22852529f56eSJonathan T. Looney {
22862529f56eSJonathan T. Looney 	struct tcp_log_dev_log_queue *entry;
22872529f56eSJonathan T. Looney 	struct tcp_log_id_bucket *tlb;
22882529f56eSJonathan T. Looney 
22892529f56eSJonathan T. Looney 	tlb = tln->tln_bucket;
22902529f56eSJonathan T. Looney 	TCPID_BUCKET_LOCK_ASSERT(tlb);
22912529f56eSJonathan T. Looney 	KASSERT(tlb->tlb_refcnt > 0,
22922529f56eSJonathan T. Looney 	    ("%s:%d: Called with unreferenced bucket (tln=%p, tlb=%p)",
22932529f56eSJonathan T. Looney 	    __func__, __LINE__, tln, tlb));
22942529f56eSJonathan T. Looney 	KASSERT(tln->tln_closed,
22952529f56eSJonathan T. Looney 	    ("%s:%d: Called for node with tln_closed==false (tln=%p)",
22962529f56eSJonathan T. Looney 	    __func__, __LINE__, tln));
22972529f56eSJonathan T. Looney 
22982529f56eSJonathan T. Looney 	/* If there are no log entries, there is nothing to do. */
22992529f56eSJonathan T. Looney 	if (tln->tln_count == 0)
23002529f56eSJonathan T. Looney 		return (0);
23012529f56eSJonathan T. Looney 
23022529f56eSJonathan T. Looney 	/*
23032529f56eSJonathan T. Looney 	 * Allocate memory. If we must wait, we'll need to drop the locks
23042529f56eSJonathan T. Looney 	 * and reacquire them (and do all the related business that goes
23052529f56eSJonathan T. Looney 	 * along with that).
23062529f56eSJonathan T. Looney 	 */
23072529f56eSJonathan T. Looney 	entry = malloc(sizeof(struct tcp_log_dev_log_queue), M_TCPLOGDEV,
23082529f56eSJonathan T. Looney 	    M_NOWAIT);
23092529f56eSJonathan T. Looney 	if (entry == NULL && (how & M_NOWAIT))
23102529f56eSJonathan T. Looney 		return (ENOBUFS);
23112529f56eSJonathan T. Looney 	if (entry == NULL) {
23122529f56eSJonathan T. Looney 		TCPID_BUCKET_UNLOCK(tlb);
23132529f56eSJonathan T. Looney 		entry = malloc(sizeof(struct tcp_log_dev_log_queue),
23142529f56eSJonathan T. Looney 		    M_TCPLOGDEV, M_WAITOK);
23152529f56eSJonathan T. Looney 		TCPID_BUCKET_LOCK(tlb);
23162529f56eSJonathan T. Looney 	}
23172529f56eSJonathan T. Looney 
23182529f56eSJonathan T. Looney 	/* Fill in the common parts of the queue entry.. */
23192529f56eSJonathan T. Looney 	entry->tldl_common.tldq_buf = NULL;
23202529f56eSJonathan T. Looney 	entry->tldl_common.tldq_xform = tcp_log_expandlogbuf;
23212529f56eSJonathan T. Looney 	entry->tldl_common.tldq_dtor = tcp_log_free_queue;
23222529f56eSJonathan T. Looney 
23232529f56eSJonathan T. Looney 	/* Fill in the unique parts of the queue entry. */
23242529f56eSJonathan T. Looney 	strlcpy(entry->tldl_id, tlb->tlb_id, TCP_LOG_ID_LEN);
2325a9a08eceSRandall Stewart 	strlcpy(entry->tldl_tag, tlb->tlb_tag, TCP_LOG_TAG_LEN);
23262529f56eSJonathan T. Looney 	if (reason != NULL)
23272529f56eSJonathan T. Looney 		strlcpy(entry->tldl_reason, reason, TCP_LOG_REASON_LEN);
23282529f56eSJonathan T. Looney 	else
23292529f56eSJonathan T. Looney 		strlcpy(entry->tldl_reason, "UNKNOWN", TCP_LOG_ID_LEN);
23302529f56eSJonathan T. Looney 	entry->tldl_ie = tln->tln_ie;
23312529f56eSJonathan T. Looney 	entry->tldl_entries = tln->tln_entries;
23322529f56eSJonathan T. Looney 	entry->tldl_count = tln->tln_count;
23332529f56eSJonathan T. Looney 	entry->tldl_af = tln->tln_af;
23342529f56eSJonathan T. Looney 
23352529f56eSJonathan T. Looney 	/* Add the entry. If no one is listening, free the entry. */
23362529f56eSJonathan T. Looney 	if (tcp_log_dev_add_log((struct tcp_log_dev_queue *)entry))
23372529f56eSJonathan T. Looney 		tcp_log_free_queue((struct tcp_log_dev_queue *)entry);
23382529f56eSJonathan T. Looney 
23392529f56eSJonathan T. Looney 	return (0);
23402529f56eSJonathan T. Looney }
23412529f56eSJonathan T. Looney 
23422529f56eSJonathan T. Looney /*
23432529f56eSJonathan T. Looney  * Queue the log buffers for all sessions in a bucket for transmissions via
23442529f56eSJonathan T. Looney  * the log buffer facility.
23452529f56eSJonathan T. Looney  *
23462529f56eSJonathan T. Looney  * NOTE: This should be called with a locked bucket; however, the function
23472529f56eSJonathan T. Looney  * will drop the lock.
23482529f56eSJonathan T. Looney  */
23492529f56eSJonathan T. Looney #define	LOCAL_SAVE	10
23502529f56eSJonathan T. Looney static void
23512529f56eSJonathan T. Looney tcp_log_dumpbucketlogs(struct tcp_log_id_bucket *tlb, char *reason)
23522529f56eSJonathan T. Looney {
23532529f56eSJonathan T. Looney 	struct tcp_log_id_node local_entries[LOCAL_SAVE];
23542529f56eSJonathan T. Looney 	struct inpcb *inp;
23552529f56eSJonathan T. Looney 	struct tcpcb *tp;
23562529f56eSJonathan T. Looney 	struct tcp_log_id_node *cur_tln, *prev_tln, *tmp_tln;
23572529f56eSJonathan T. Looney 	int i, num_local_entries, tree_locked;
23582529f56eSJonathan T. Looney 	bool expireq_locked;
23592529f56eSJonathan T. Looney 
23602529f56eSJonathan T. Looney 	TCPID_BUCKET_LOCK_ASSERT(tlb);
23612529f56eSJonathan T. Looney 
23622529f56eSJonathan T. Looney 	/*
23632529f56eSJonathan T. Looney 	 * Take a reference on the bucket to keep it from disappearing until
23642529f56eSJonathan T. Looney 	 * we are done.
23652529f56eSJonathan T. Looney 	 */
23662529f56eSJonathan T. Looney 	TCPID_BUCKET_REF(tlb);
23672529f56eSJonathan T. Looney 
23682529f56eSJonathan T. Looney 	/*
23692529f56eSJonathan T. Looney 	 * We'll try to create these without dropping locks. However, we
23702529f56eSJonathan T. Looney 	 * might very well need to drop locks to get memory. If that's the
23712529f56eSJonathan T. Looney 	 * case, we'll save up to 10 on the stack, and sacrifice the rest.
23722529f56eSJonathan T. Looney 	 * (Otherwise, we need to worry about finding our place again in a
23732529f56eSJonathan T. Looney 	 * potentially changed list. It just doesn't seem worth the trouble
23742529f56eSJonathan T. Looney 	 * to do that.
23752529f56eSJonathan T. Looney 	 */
23762529f56eSJonathan T. Looney 	expireq_locked = false;
23772529f56eSJonathan T. Looney 	num_local_entries = 0;
23782529f56eSJonathan T. Looney 	prev_tln = NULL;
23792529f56eSJonathan T. Looney 	tree_locked = TREE_UNLOCKED;
23802529f56eSJonathan T. Looney 	SLIST_FOREACH_SAFE(cur_tln, &tlb->tlb_head, tln_list, tmp_tln) {
23812529f56eSJonathan T. Looney 		/*
23822529f56eSJonathan T. Looney 		 * If this isn't associated with a TCPCB, we can pull it off
23832529f56eSJonathan T. Looney 		 * the list now. We need to be careful that the expire timer
23842529f56eSJonathan T. Looney 		 * hasn't already taken ownership (tln_expiretime == SBT_MAX).
23852529f56eSJonathan T. Looney 		 * If so, we let the expire timer code free the data.
23862529f56eSJonathan T. Looney 		 */
23872529f56eSJonathan T. Looney 		if (cur_tln->tln_closed) {
23882529f56eSJonathan T. Looney no_inp:
23892529f56eSJonathan T. Looney 			/*
23902529f56eSJonathan T. Looney 			 * Get the expireq lock so we can get a consistent
23912529f56eSJonathan T. Looney 			 * read of tln_expiretime and so we can remove this
23922529f56eSJonathan T. Looney 			 * from the expireq.
23932529f56eSJonathan T. Looney 			 */
23942529f56eSJonathan T. Looney 			if (!expireq_locked) {
23952529f56eSJonathan T. Looney 				TCPLOG_EXPIREQ_LOCK();
23962529f56eSJonathan T. Looney 				expireq_locked = true;
23972529f56eSJonathan T. Looney 			}
23982529f56eSJonathan T. Looney 
23992529f56eSJonathan T. Looney 			/*
24002529f56eSJonathan T. Looney 			 * We ignore entries with tln_expiretime == SBT_MAX.
24012529f56eSJonathan T. Looney 			 * The expire timer code already owns those.
24022529f56eSJonathan T. Looney 			 */
24032529f56eSJonathan T. Looney 			KASSERT(cur_tln->tln_expiretime > (sbintime_t) 0,
24042529f56eSJonathan T. Looney 			    ("%s:%d: node on the expire queue without positive "
24052529f56eSJonathan T. Looney 			    "expire time", __func__, __LINE__));
24062529f56eSJonathan T. Looney 			if (cur_tln->tln_expiretime == SBT_MAX) {
24072529f56eSJonathan T. Looney 				prev_tln = cur_tln;
24082529f56eSJonathan T. Looney 				continue;
24092529f56eSJonathan T. Looney 			}
24102529f56eSJonathan T. Looney 
24112529f56eSJonathan T. Looney 			/* Remove the entry from the expireq. */
24122529f56eSJonathan T. Looney 			STAILQ_REMOVE(&tcp_log_expireq_head, cur_tln,
24132529f56eSJonathan T. Looney 			    tcp_log_id_node, tln_expireq);
24142529f56eSJonathan T. Looney 
24152529f56eSJonathan T. Looney 			/* Remove the entry from the bucket. */
24162529f56eSJonathan T. Looney 			if (prev_tln != NULL)
24172529f56eSJonathan T. Looney 				SLIST_REMOVE_AFTER(prev_tln, tln_list);
24182529f56eSJonathan T. Looney 			else
24192529f56eSJonathan T. Looney 				SLIST_REMOVE_HEAD(&tlb->tlb_head, tln_list);
24202529f56eSJonathan T. Looney 
24212529f56eSJonathan T. Looney 			/*
24222529f56eSJonathan T. Looney 			 * Drop the INP and bucket reference counts. Due to
24232529f56eSJonathan T. Looney 			 * lock-ordering rules, we need to drop the expire
24242529f56eSJonathan T. Looney 			 * queue lock.
24252529f56eSJonathan T. Looney 			 */
24262529f56eSJonathan T. Looney 			TCPLOG_EXPIREQ_UNLOCK();
24272529f56eSJonathan T. Looney 			expireq_locked = false;
24282529f56eSJonathan T. Looney 
24292529f56eSJonathan T. Looney 			/* Drop the INP reference. */
24302529f56eSJonathan T. Looney 			INP_WLOCK(cur_tln->tln_inp);
24312529f56eSJonathan T. Looney 			if (!in_pcbrele_wlocked(cur_tln->tln_inp))
24322529f56eSJonathan T. Looney 				INP_WUNLOCK(cur_tln->tln_inp);
24332529f56eSJonathan T. Looney 
24342529f56eSJonathan T. Looney 			if (tcp_log_unref_bucket(tlb, &tree_locked, NULL)) {
24352529f56eSJonathan T. Looney #ifdef INVARIANTS
24362529f56eSJonathan T. Looney 				panic("%s: Bucket refcount unexpectedly 0.",
24372529f56eSJonathan T. Looney 				    __func__);
24382529f56eSJonathan T. Looney #endif
24392529f56eSJonathan T. Looney 				/*
24402529f56eSJonathan T. Looney 				 * Recover as best we can: free the entry we
24412529f56eSJonathan T. Looney 				 * own.
24422529f56eSJonathan T. Looney 				 */
24432529f56eSJonathan T. Looney 				tcp_log_free_entries(&cur_tln->tln_entries,
24442529f56eSJonathan T. Looney 				    &cur_tln->tln_count);
24458c47d8f5SAlan Somers 				uma_zfree(tcp_log_id_node_zone, cur_tln);
24462529f56eSJonathan T. Looney 				goto done;
24472529f56eSJonathan T. Looney 			}
24482529f56eSJonathan T. Looney 
24492529f56eSJonathan T. Looney 			if (tcp_log_dump_node_logbuf(cur_tln, reason,
24502529f56eSJonathan T. Looney 			    M_NOWAIT)) {
24512529f56eSJonathan T. Looney 				/*
24522529f56eSJonathan T. Looney 				 * If we have sapce, save the entries locally.
24532529f56eSJonathan T. Looney 				 * Otherwise, free them.
24542529f56eSJonathan T. Looney 				 */
24552529f56eSJonathan T. Looney 				if (num_local_entries < LOCAL_SAVE) {
24562529f56eSJonathan T. Looney 					local_entries[num_local_entries] =
24572529f56eSJonathan T. Looney 					    *cur_tln;
24582529f56eSJonathan T. Looney 					num_local_entries++;
24592529f56eSJonathan T. Looney 				} else {
24602529f56eSJonathan T. Looney 					tcp_log_free_entries(
24612529f56eSJonathan T. Looney 					    &cur_tln->tln_entries,
24622529f56eSJonathan T. Looney 					    &cur_tln->tln_count);
24632529f56eSJonathan T. Looney 				}
24642529f56eSJonathan T. Looney 			}
24652529f56eSJonathan T. Looney 
24662529f56eSJonathan T. Looney 			/* No matter what, we are done with the node now. */
24678c47d8f5SAlan Somers 			uma_zfree(tcp_log_id_node_zone, cur_tln);
24682529f56eSJonathan T. Looney 
24692529f56eSJonathan T. Looney 			/*
24702529f56eSJonathan T. Looney 			 * Because we removed this entry from the list, prev_tln
24712529f56eSJonathan T. Looney 			 * (which tracks the previous entry still on the tlb
24722529f56eSJonathan T. Looney 			 * list) remains unchanged.
24732529f56eSJonathan T. Looney 			 */
24742529f56eSJonathan T. Looney 			continue;
24752529f56eSJonathan T. Looney 		}
24762529f56eSJonathan T. Looney 
24772529f56eSJonathan T. Looney 		/*
24782529f56eSJonathan T. Looney 		 * If we get to this point, the session data is still held in
24792529f56eSJonathan T. Looney 		 * the TCPCB. So, we need to pull the data out of that.
24802529f56eSJonathan T. Looney 		 *
24812529f56eSJonathan T. Looney 		 * We will need to drop the expireq lock so we can lock the INP.
24822529f56eSJonathan T. Looney 		 * We can then try to extract the data the "easy" way. If that
24832529f56eSJonathan T. Looney 		 * fails, we'll save the log entries for later.
24842529f56eSJonathan T. Looney 		 */
24852529f56eSJonathan T. Looney 		if (expireq_locked) {
24862529f56eSJonathan T. Looney 			TCPLOG_EXPIREQ_UNLOCK();
24872529f56eSJonathan T. Looney 			expireq_locked = false;
24882529f56eSJonathan T. Looney 		}
24892529f56eSJonathan T. Looney 
24902529f56eSJonathan T. Looney 		/* Lock the INP and then re-check the state. */
24912529f56eSJonathan T. Looney 		inp = cur_tln->tln_inp;
24922529f56eSJonathan T. Looney 		INP_WLOCK(inp);
24932529f56eSJonathan T. Looney 		/*
24942529f56eSJonathan T. Looney 		 * If we caught this while it was transitioning, the data
24952529f56eSJonathan T. Looney 		 * might have moved from the TCPCB to the tln (signified by
24962529f56eSJonathan T. Looney 		 * setting tln_closed to true. If so, treat this like an
24972529f56eSJonathan T. Looney 		 * inactive connection.
24982529f56eSJonathan T. Looney 		 */
24992529f56eSJonathan T. Looney 		if (cur_tln->tln_closed) {
25002529f56eSJonathan T. Looney 			/*
25012529f56eSJonathan T. Looney 			 * It looks like we may have caught this connection
25022529f56eSJonathan T. Looney 			 * while it was transitioning from active to inactive.
25032529f56eSJonathan T. Looney 			 * Treat this like an inactive connection.
25042529f56eSJonathan T. Looney 			 */
25052529f56eSJonathan T. Looney 			INP_WUNLOCK(inp);
25062529f56eSJonathan T. Looney 			goto no_inp;
25072529f56eSJonathan T. Looney 		}
25082529f56eSJonathan T. Looney 
25092529f56eSJonathan T. Looney 		/*
25102529f56eSJonathan T. Looney 		 * Try to dump the data from the tp without dropping the lock.
25112529f56eSJonathan T. Looney 		 * If this fails, try to save off the data locally.
25122529f56eSJonathan T. Looney 		 */
25132529f56eSJonathan T. Looney 		tp = cur_tln->tln_tp;
25142529f56eSJonathan T. Looney 		if (tcp_log_dump_tp_logbuf(tp, reason, M_NOWAIT, true) &&
25152529f56eSJonathan T. Looney 		    num_local_entries < LOCAL_SAVE) {
25162529f56eSJonathan T. Looney 			tcp_log_move_tp_to_node(tp,
25172529f56eSJonathan T. Looney 			    &local_entries[num_local_entries]);
25182529f56eSJonathan T. Looney 			local_entries[num_local_entries].tln_closed = 1;
25192529f56eSJonathan T. Looney 			KASSERT(local_entries[num_local_entries].tln_bucket ==
25202529f56eSJonathan T. Looney 			    tlb, ("%s: %d: bucket mismatch for node %p",
25212529f56eSJonathan T. Looney 			    __func__, __LINE__, cur_tln));
25222529f56eSJonathan T. Looney 			num_local_entries++;
25232529f56eSJonathan T. Looney 		}
25242529f56eSJonathan T. Looney 
25252529f56eSJonathan T. Looney 		INP_WUNLOCK(inp);
25262529f56eSJonathan T. Looney 
25272529f56eSJonathan T. Looney 		/*
25282529f56eSJonathan T. Looney 		 * We are goint to leave the current tln on the list. It will
25292529f56eSJonathan T. Looney 		 * become the previous tln.
25302529f56eSJonathan T. Looney 		 */
25312529f56eSJonathan T. Looney 		prev_tln = cur_tln;
25322529f56eSJonathan T. Looney 	}
25332529f56eSJonathan T. Looney 
25342529f56eSJonathan T. Looney 	/* Drop our locks, if any. */
25352529f56eSJonathan T. Looney 	KASSERT(tree_locked == TREE_UNLOCKED,
25362529f56eSJonathan T. Looney 	    ("%s: %d: tree unexpectedly locked", __func__, __LINE__));
25372529f56eSJonathan T. Looney 	switch (tree_locked) {
25382529f56eSJonathan T. Looney 	case TREE_WLOCKED:
25392529f56eSJonathan T. Looney 		TCPID_TREE_WUNLOCK();
25402529f56eSJonathan T. Looney 		tree_locked = TREE_UNLOCKED;
25412529f56eSJonathan T. Looney 		break;
25422529f56eSJonathan T. Looney 	case TREE_RLOCKED:
25432529f56eSJonathan T. Looney 		TCPID_TREE_RUNLOCK();
25442529f56eSJonathan T. Looney 		tree_locked = TREE_UNLOCKED;
25452529f56eSJonathan T. Looney 		break;
25462529f56eSJonathan T. Looney 	}
25472529f56eSJonathan T. Looney 	if (expireq_locked) {
25482529f56eSJonathan T. Looney 		TCPLOG_EXPIREQ_UNLOCK();
25492529f56eSJonathan T. Looney 		expireq_locked = false;
25502529f56eSJonathan T. Looney 	}
25512529f56eSJonathan T. Looney 
25522529f56eSJonathan T. Looney 	/*
25532529f56eSJonathan T. Looney 	 * Try again for any saved entries. tcp_log_dump_node_logbuf() is
25542529f56eSJonathan T. Looney 	 * guaranteed to free the log entries within the node. And, since
25552529f56eSJonathan T. Looney 	 * the node itself is on our stack, we don't need to free it.
25562529f56eSJonathan T. Looney 	 */
25572529f56eSJonathan T. Looney 	for (i = 0; i < num_local_entries; i++)
25582529f56eSJonathan T. Looney 		tcp_log_dump_node_logbuf(&local_entries[i], reason, M_WAITOK);
25592529f56eSJonathan T. Looney 
25602529f56eSJonathan T. Looney 	/* Drop our reference. */
25612529f56eSJonathan T. Looney 	if (!tcp_log_unref_bucket(tlb, &tree_locked, NULL))
25622529f56eSJonathan T. Looney 		TCPID_BUCKET_UNLOCK(tlb);
25632529f56eSJonathan T. Looney 
25642529f56eSJonathan T. Looney done:
25652529f56eSJonathan T. Looney 	/* Drop our locks, if any. */
25662529f56eSJonathan T. Looney 	switch (tree_locked) {
25672529f56eSJonathan T. Looney 	case TREE_WLOCKED:
25682529f56eSJonathan T. Looney 		TCPID_TREE_WUNLOCK();
25692529f56eSJonathan T. Looney 		break;
25702529f56eSJonathan T. Looney 	case TREE_RLOCKED:
25712529f56eSJonathan T. Looney 		TCPID_TREE_RUNLOCK();
25722529f56eSJonathan T. Looney 		break;
25732529f56eSJonathan T. Looney 	}
25742529f56eSJonathan T. Looney 	if (expireq_locked)
25752529f56eSJonathan T. Looney 		TCPLOG_EXPIREQ_UNLOCK();
25762529f56eSJonathan T. Looney }
25772529f56eSJonathan T. Looney #undef	LOCAL_SAVE
25782529f56eSJonathan T. Looney 
25792529f56eSJonathan T. Looney /*
25802529f56eSJonathan T. Looney  * Queue the log buffers for all sessions in a bucket for transmissions via
25812529f56eSJonathan T. Looney  * the log buffer facility.
25822529f56eSJonathan T. Looney  *
25832529f56eSJonathan T. Looney  * NOTE: This should be called with a locked INP; however, the function
25842529f56eSJonathan T. Looney  * will drop the lock.
25852529f56eSJonathan T. Looney  */
25862529f56eSJonathan T. Looney void
25872529f56eSJonathan T. Looney tcp_log_dump_tp_bucket_logbufs(struct tcpcb *tp, char *reason)
25882529f56eSJonathan T. Looney {
25899eb0e832SGleb Smirnoff 	struct inpcb *inp = tptoinpcb(tp);
25902529f56eSJonathan T. Looney 	struct tcp_log_id_bucket *tlb;
25912529f56eSJonathan T. Looney 	int tree_locked;
25922529f56eSJonathan T. Looney 
25932529f56eSJonathan T. Looney 	/* Figure out our bucket and lock it. */
25949eb0e832SGleb Smirnoff 	INP_WLOCK_ASSERT(inp);
25952529f56eSJonathan T. Looney 	tlb = tp->t_lib;
25962529f56eSJonathan T. Looney 	if (tlb == NULL) {
25972529f56eSJonathan T. Looney 		/*
25982529f56eSJonathan T. Looney 		 * No bucket; treat this like a request to dump a single
25992529f56eSJonathan T. Looney 		 * session's traces.
26002529f56eSJonathan T. Looney 		 */
26012529f56eSJonathan T. Looney 		(void)tcp_log_dump_tp_logbuf(tp, reason, M_WAITOK, true);
26029eb0e832SGleb Smirnoff 		INP_WUNLOCK(inp);
26032529f56eSJonathan T. Looney 		return;
26042529f56eSJonathan T. Looney 	}
26052529f56eSJonathan T. Looney 	TCPID_BUCKET_REF(tlb);
26069eb0e832SGleb Smirnoff 	INP_WUNLOCK(inp);
26072529f56eSJonathan T. Looney 	TCPID_BUCKET_LOCK(tlb);
26082529f56eSJonathan T. Looney 
26092529f56eSJonathan T. Looney 	/* If we are the last reference, we have nothing more to do here. */
26102529f56eSJonathan T. Looney 	tree_locked = TREE_UNLOCKED;
26112529f56eSJonathan T. Looney 	if (tcp_log_unref_bucket(tlb, &tree_locked, NULL)) {
26122529f56eSJonathan T. Looney 		switch (tree_locked) {
26132529f56eSJonathan T. Looney 		case TREE_WLOCKED:
26142529f56eSJonathan T. Looney 			TCPID_TREE_WUNLOCK();
26152529f56eSJonathan T. Looney 			break;
26162529f56eSJonathan T. Looney 		case TREE_RLOCKED:
26172529f56eSJonathan T. Looney 			TCPID_TREE_RUNLOCK();
26182529f56eSJonathan T. Looney 			break;
26192529f56eSJonathan T. Looney 		}
26202529f56eSJonathan T. Looney 		return;
26212529f56eSJonathan T. Looney 	}
26222529f56eSJonathan T. Looney 
26232529f56eSJonathan T. Looney 	/* Turn this over to tcp_log_dumpbucketlogs() to finish the work. */
26242529f56eSJonathan T. Looney 	tcp_log_dumpbucketlogs(tlb, reason);
26252529f56eSJonathan T. Looney }
26262529f56eSJonathan T. Looney 
26272529f56eSJonathan T. Looney /*
26282529f56eSJonathan T. Looney  * Mark the end of a flow with the current stack. A stack can add
26292529f56eSJonathan T. Looney  * stack-specific info to this trace event by overriding this
26302529f56eSJonathan T. Looney  * function (see bbr_log_flowend() for example).
26312529f56eSJonathan T. Looney  */
26322529f56eSJonathan T. Looney void
26332529f56eSJonathan T. Looney tcp_log_flowend(struct tcpcb *tp)
26342529f56eSJonathan T. Looney {
26352529f56eSJonathan T. Looney 	if (tp->t_logstate != TCP_LOG_STATE_OFF) {
26369eb0e832SGleb Smirnoff 		struct socket *so = tptosocket(tp);
26372529f56eSJonathan T. Looney 		TCP_LOG_EVENT(tp, NULL, &so->so_rcv, &so->so_snd,
26382529f56eSJonathan T. Looney 				TCP_LOG_FLOWEND, 0, 0, NULL, false);
26392529f56eSJonathan T. Looney 	}
26402529f56eSJonathan T. Looney }
2641