12529f56eSJonathan T. Looney /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
32529f56eSJonathan T. Looney *
452467047SWarner Losh * Copyright (c) 2016-2017 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/param.h>
302529f56eSJonathan T. Looney #include <sys/conf.h>
312529f56eSJonathan T. Looney #include <sys/fcntl.h>
322529f56eSJonathan T. Looney #include <sys/filio.h>
332529f56eSJonathan T. Looney #include <sys/kernel.h>
342529f56eSJonathan T. Looney #include <sys/lock.h>
352529f56eSJonathan T. Looney #include <sys/malloc.h>
362529f56eSJonathan T. Looney #include <sys/module.h>
372529f56eSJonathan T. Looney #include <sys/poll.h>
382529f56eSJonathan T. Looney #include <sys/queue.h>
392529f56eSJonathan T. Looney #include <sys/refcount.h>
402529f56eSJonathan T. Looney #include <sys/mutex.h>
412529f56eSJonathan T. Looney #include <sys/selinfo.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>
462529f56eSJonathan T. Looney #include <sys/uio.h>
472529f56eSJonathan T. Looney #include <machine/atomic.h>
482529f56eSJonathan T. Looney #include <sys/counter.h>
492529f56eSJonathan T. Looney
502529f56eSJonathan T. Looney #include <dev/tcp_log/tcp_log_dev.h>
512529f56eSJonathan T. Looney
522529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
532529f56eSJonathan T. Looney extern counter_u64_t tcp_log_que_read;
542529f56eSJonathan T. Looney extern counter_u64_t tcp_log_que_freed;
552529f56eSJonathan T. Looney #endif
562529f56eSJonathan T. Looney
572529f56eSJonathan T. Looney static struct cdev *tcp_log_dev;
582529f56eSJonathan T. Looney static struct selinfo tcp_log_sel;
592529f56eSJonathan T. Looney
602529f56eSJonathan T. Looney static struct log_queueh tcp_log_dev_queue_head = STAILQ_HEAD_INITIALIZER(tcp_log_dev_queue_head);
612529f56eSJonathan T. Looney static struct log_infoh tcp_log_dev_reader_head = STAILQ_HEAD_INITIALIZER(tcp_log_dev_reader_head);
622529f56eSJonathan T. Looney
632529f56eSJonathan T. Looney MALLOC_DEFINE(M_TCPLOGDEV, "tcp_log_dev", "TCP log device data structures");
642529f56eSJonathan T. Looney
652529f56eSJonathan T. Looney static int tcp_log_dev_listeners = 0;
662529f56eSJonathan T. Looney
672529f56eSJonathan T. Looney static struct mtx tcp_log_dev_queue_lock;
682529f56eSJonathan T. Looney
692529f56eSJonathan T. Looney #define TCP_LOG_DEV_QUEUE_LOCK() mtx_lock(&tcp_log_dev_queue_lock)
702529f56eSJonathan T. Looney #define TCP_LOG_DEV_QUEUE_UNLOCK() mtx_unlock(&tcp_log_dev_queue_lock)
712529f56eSJonathan T. Looney #define TCP_LOG_DEV_QUEUE_LOCK_ASSERT() mtx_assert(&tcp_log_dev_queue_lock, MA_OWNED)
722529f56eSJonathan T. Looney #define TCP_LOG_DEV_QUEUE_UNLOCK_ASSERT() mtx_assert(&tcp_log_dev_queue_lock, MA_NOTOWNED)
732529f56eSJonathan T. Looney #define TCP_LOG_DEV_QUEUE_REF(tldq) refcount_acquire(&((tldq)->tldq_refcnt))
742529f56eSJonathan T. Looney #define TCP_LOG_DEV_QUEUE_UNREF(tldq) refcount_release(&((tldq)->tldq_refcnt))
752529f56eSJonathan T. Looney
762529f56eSJonathan T. Looney static void tcp_log_dev_clear_refcount(struct tcp_log_dev_queue *entry);
772529f56eSJonathan T. Looney static void tcp_log_dev_clear_cdevpriv(void *data);
782529f56eSJonathan T. Looney static int tcp_log_dev_open(struct cdev *dev __unused, int flags,
792529f56eSJonathan T. Looney int devtype __unused, struct thread *td __unused);
802529f56eSJonathan T. Looney static int tcp_log_dev_write(struct cdev *dev __unused,
812529f56eSJonathan T. Looney struct uio *uio __unused, int flags __unused);
822529f56eSJonathan T. Looney static int tcp_log_dev_read(struct cdev *dev __unused, struct uio *uio,
832529f56eSJonathan T. Looney int flags __unused);
842529f56eSJonathan T. Looney static int tcp_log_dev_ioctl(struct cdev *dev __unused, u_long cmd,
852529f56eSJonathan T. Looney caddr_t data, int fflag __unused, struct thread *td __unused);
862529f56eSJonathan T. Looney static int tcp_log_dev_poll(struct cdev *dev __unused, int events,
872529f56eSJonathan T. Looney struct thread *td);
882529f56eSJonathan T. Looney
892529f56eSJonathan T. Looney enum tcp_log_dev_queue_lock_state {
902529f56eSJonathan T. Looney QUEUE_UNLOCKED = 0,
912529f56eSJonathan T. Looney QUEUE_LOCKED,
922529f56eSJonathan T. Looney };
932529f56eSJonathan T. Looney
942529f56eSJonathan T. Looney static struct cdevsw tcp_log_cdevsw = {
952529f56eSJonathan T. Looney .d_version = D_VERSION,
962529f56eSJonathan T. Looney .d_read = tcp_log_dev_read,
972529f56eSJonathan T. Looney .d_open = tcp_log_dev_open,
982529f56eSJonathan T. Looney .d_write = tcp_log_dev_write,
992529f56eSJonathan T. Looney .d_poll = tcp_log_dev_poll,
1002529f56eSJonathan T. Looney .d_ioctl = tcp_log_dev_ioctl,
1012529f56eSJonathan T. Looney #ifdef NOTYET
1022529f56eSJonathan T. Looney .d_mmap = tcp_log_dev_mmap,
1032529f56eSJonathan T. Looney #endif
1042529f56eSJonathan T. Looney .d_name = "tcp_log",
1052529f56eSJonathan T. Looney };
1062529f56eSJonathan T. Looney
1072529f56eSJonathan T. Looney static __inline void
tcp_log_dev_queue_validate_lock(int lockstate)1082529f56eSJonathan T. Looney tcp_log_dev_queue_validate_lock(int lockstate)
1092529f56eSJonathan T. Looney {
1102529f56eSJonathan T. Looney
1112529f56eSJonathan T. Looney #ifdef INVARIANTS
1122529f56eSJonathan T. Looney switch (lockstate) {
1132529f56eSJonathan T. Looney case QUEUE_LOCKED:
1142529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK_ASSERT();
1152529f56eSJonathan T. Looney break;
1162529f56eSJonathan T. Looney case QUEUE_UNLOCKED:
1172529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_UNLOCK_ASSERT();
1182529f56eSJonathan T. Looney break;
1192529f56eSJonathan T. Looney default:
1202529f56eSJonathan T. Looney kassert_panic("%s:%d: unknown queue lock state", __func__,
1212529f56eSJonathan T. Looney __LINE__);
1222529f56eSJonathan T. Looney }
1232529f56eSJonathan T. Looney #endif
1242529f56eSJonathan T. Looney }
1252529f56eSJonathan T. Looney
1262529f56eSJonathan T. Looney /*
1272529f56eSJonathan T. Looney * Clear the refcount. If appropriate, it will remove the entry from the
1282529f56eSJonathan T. Looney * queue and call the destructor.
1292529f56eSJonathan T. Looney *
1302529f56eSJonathan T. Looney * This must be called with the queue lock held.
1312529f56eSJonathan T. Looney */
1322529f56eSJonathan T. Looney static void
tcp_log_dev_clear_refcount(struct tcp_log_dev_queue * entry)1332529f56eSJonathan T. Looney tcp_log_dev_clear_refcount(struct tcp_log_dev_queue *entry)
1342529f56eSJonathan T. Looney {
1352529f56eSJonathan T. Looney
1362529f56eSJonathan T. Looney KASSERT(entry != NULL, ("%s: called with NULL entry", __func__));
1372529f56eSJonathan T. Looney
1382529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK_ASSERT();
1392529f56eSJonathan T. Looney
1402529f56eSJonathan T. Looney if (TCP_LOG_DEV_QUEUE_UNREF(entry)) {
1412529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
1422529f56eSJonathan T. Looney counter_u64_add(tcp_log_que_freed, 1);
1432529f56eSJonathan T. Looney #endif
1442529f56eSJonathan T. Looney /* Remove the entry from the queue and call the destructor. */
1452529f56eSJonathan T. Looney STAILQ_REMOVE(&tcp_log_dev_queue_head, entry, tcp_log_dev_queue,
1462529f56eSJonathan T. Looney tldq_queue);
1472529f56eSJonathan T. Looney (*entry->tldq_dtor)(entry);
1482529f56eSJonathan T. Looney }
1492529f56eSJonathan T. Looney }
1502529f56eSJonathan T. Looney
1512529f56eSJonathan T. Looney static void
tcp_log_dev_clear_cdevpriv(void * data)1522529f56eSJonathan T. Looney tcp_log_dev_clear_cdevpriv(void *data)
1532529f56eSJonathan T. Looney {
1542529f56eSJonathan T. Looney struct tcp_log_dev_info *priv;
1552529f56eSJonathan T. Looney struct tcp_log_dev_queue *entry, *entry_tmp;
1562529f56eSJonathan T. Looney
1572529f56eSJonathan T. Looney priv = (struct tcp_log_dev_info *)data;
1582529f56eSJonathan T. Looney if (priv == NULL)
1592529f56eSJonathan T. Looney return;
1602529f56eSJonathan T. Looney
1612529f56eSJonathan T. Looney /*
1622529f56eSJonathan T. Looney * Lock the queue and drop our references. We hold references to all
1632529f56eSJonathan T. Looney * the entries starting with tldi_head (or, if tldi_head == NULL, all
1642529f56eSJonathan T. Looney * entries in the queue).
1652529f56eSJonathan T. Looney *
1662529f56eSJonathan T. Looney * Because we don't want anyone adding addition things to the queue
1672529f56eSJonathan T. Looney * while we are doing this, we lock the queue.
1682529f56eSJonathan T. Looney */
1692529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK();
1702529f56eSJonathan T. Looney if (priv->tldi_head != NULL) {
1712529f56eSJonathan T. Looney entry = priv->tldi_head;
1722529f56eSJonathan T. Looney STAILQ_FOREACH_FROM_SAFE(entry, &tcp_log_dev_queue_head,
1732529f56eSJonathan T. Looney tldq_queue, entry_tmp) {
1742529f56eSJonathan T. Looney tcp_log_dev_clear_refcount(entry);
1752529f56eSJonathan T. Looney }
1762529f56eSJonathan T. Looney }
1772529f56eSJonathan T. Looney tcp_log_dev_listeners--;
1782529f56eSJonathan T. Looney KASSERT(tcp_log_dev_listeners >= 0,
1792529f56eSJonathan T. Looney ("%s: tcp_log_dev_listeners is unexpectedly negative", __func__));
1802529f56eSJonathan T. Looney STAILQ_REMOVE(&tcp_log_dev_reader_head, priv, tcp_log_dev_info,
1812529f56eSJonathan T. Looney tldi_list);
1822529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK_ASSERT();
1832529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_UNLOCK();
1842529f56eSJonathan T. Looney free(priv, M_TCPLOGDEV);
1852529f56eSJonathan T. Looney }
1862529f56eSJonathan T. Looney
1872529f56eSJonathan T. Looney static int
tcp_log_dev_open(struct cdev * dev __unused,int flags,int devtype __unused,struct thread * td __unused)1882529f56eSJonathan T. Looney tcp_log_dev_open(struct cdev *dev __unused, int flags, int devtype __unused,
1892529f56eSJonathan T. Looney struct thread *td __unused)
1902529f56eSJonathan T. Looney {
1912529f56eSJonathan T. Looney struct tcp_log_dev_info *priv;
1922529f56eSJonathan T. Looney struct tcp_log_dev_queue *entry;
1932529f56eSJonathan T. Looney int rv;
1942529f56eSJonathan T. Looney
1952529f56eSJonathan T. Looney /*
1962529f56eSJonathan T. Looney * Ideally, we shouldn't see these because of file system
1972529f56eSJonathan T. Looney * permissions.
1982529f56eSJonathan T. Looney */
1992529f56eSJonathan T. Looney if (flags & (FWRITE | FEXEC | FAPPEND | O_TRUNC))
2002529f56eSJonathan T. Looney return (ENODEV);
2012529f56eSJonathan T. Looney
2022529f56eSJonathan T. Looney /* Allocate space to hold information about where we are. */
2032529f56eSJonathan T. Looney priv = malloc(sizeof(struct tcp_log_dev_info), M_TCPLOGDEV,
2042529f56eSJonathan T. Looney M_ZERO | M_WAITOK);
2052529f56eSJonathan T. Looney
2062529f56eSJonathan T. Looney /* Stash the private data away. */
2072529f56eSJonathan T. Looney rv = devfs_set_cdevpriv((void *)priv, tcp_log_dev_clear_cdevpriv);
2082529f56eSJonathan T. Looney if (!rv) {
2092529f56eSJonathan T. Looney /*
2102529f56eSJonathan T. Looney * Increase the listener count, add this reader to the list, and
2112529f56eSJonathan T. Looney * take references on all current queues.
2122529f56eSJonathan T. Looney */
2132529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK();
2142529f56eSJonathan T. Looney tcp_log_dev_listeners++;
2152529f56eSJonathan T. Looney STAILQ_INSERT_HEAD(&tcp_log_dev_reader_head, priv, tldi_list);
2162529f56eSJonathan T. Looney priv->tldi_head = STAILQ_FIRST(&tcp_log_dev_queue_head);
2172529f56eSJonathan T. Looney if (priv->tldi_head != NULL)
2182529f56eSJonathan T. Looney priv->tldi_cur = priv->tldi_head->tldq_buf;
2192529f56eSJonathan T. Looney STAILQ_FOREACH(entry, &tcp_log_dev_queue_head, tldq_queue)
2202529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_REF(entry);
2212529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_UNLOCK();
2222529f56eSJonathan T. Looney } else {
2232529f56eSJonathan T. Looney /* Free the entry. */
2242529f56eSJonathan T. Looney free(priv, M_TCPLOGDEV);
2252529f56eSJonathan T. Looney }
2262529f56eSJonathan T. Looney return (rv);
2272529f56eSJonathan T. Looney }
2282529f56eSJonathan T. Looney
2292529f56eSJonathan T. Looney static int
tcp_log_dev_write(struct cdev * dev __unused,struct uio * uio __unused,int flags __unused)2302529f56eSJonathan T. Looney tcp_log_dev_write(struct cdev *dev __unused, struct uio *uio __unused,
2312529f56eSJonathan T. Looney int flags __unused)
2322529f56eSJonathan T. Looney {
2332529f56eSJonathan T. Looney
2342529f56eSJonathan T. Looney return (ENODEV);
2352529f56eSJonathan T. Looney }
2362529f56eSJonathan T. Looney
2372529f56eSJonathan T. Looney static __inline void
tcp_log_dev_rotate_bufs(struct tcp_log_dev_info * priv,int * lockstate)2382529f56eSJonathan T. Looney tcp_log_dev_rotate_bufs(struct tcp_log_dev_info *priv, int *lockstate)
2392529f56eSJonathan T. Looney {
2402529f56eSJonathan T. Looney struct tcp_log_dev_queue *entry;
2412529f56eSJonathan T. Looney
2422529f56eSJonathan T. Looney KASSERT(priv->tldi_head != NULL,
2432529f56eSJonathan T. Looney ("%s:%d: priv->tldi_head unexpectedly NULL",
2442529f56eSJonathan T. Looney __func__, __LINE__));
2452529f56eSJonathan T. Looney KASSERT(priv->tldi_head->tldq_buf == priv->tldi_cur,
2462529f56eSJonathan T. Looney ("%s:%d: buffer mismatch (%p vs %p)",
2472529f56eSJonathan T. Looney __func__, __LINE__, priv->tldi_head->tldq_buf,
2482529f56eSJonathan T. Looney priv->tldi_cur));
2492529f56eSJonathan T. Looney tcp_log_dev_queue_validate_lock(*lockstate);
2502529f56eSJonathan T. Looney
2512529f56eSJonathan T. Looney if (*lockstate == QUEUE_UNLOCKED) {
2522529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK();
2532529f56eSJonathan T. Looney *lockstate = QUEUE_LOCKED;
2542529f56eSJonathan T. Looney }
2552529f56eSJonathan T. Looney entry = priv->tldi_head;
2562529f56eSJonathan T. Looney priv->tldi_head = STAILQ_NEXT(entry, tldq_queue);
2572529f56eSJonathan T. Looney tcp_log_dev_clear_refcount(entry);
2582529f56eSJonathan T. Looney priv->tldi_cur = NULL;
2592529f56eSJonathan T. Looney }
2602529f56eSJonathan T. Looney
2612529f56eSJonathan T. Looney static int
tcp_log_dev_read(struct cdev * dev __unused,struct uio * uio,int flags)2622529f56eSJonathan T. Looney tcp_log_dev_read(struct cdev *dev __unused, struct uio *uio, int flags)
2632529f56eSJonathan T. Looney {
2642529f56eSJonathan T. Looney struct tcp_log_common_header *buf;
2652529f56eSJonathan T. Looney struct tcp_log_dev_info *priv;
2662529f56eSJonathan T. Looney struct tcp_log_dev_queue *entry;
2672529f56eSJonathan T. Looney ssize_t len;
2682529f56eSJonathan T. Looney int lockstate, rv;
2692529f56eSJonathan T. Looney
2702529f56eSJonathan T. Looney /* Get our private info. */
2712529f56eSJonathan T. Looney rv = devfs_get_cdevpriv((void **)&priv);
2722529f56eSJonathan T. Looney if (rv)
2732529f56eSJonathan T. Looney return (rv);
2742529f56eSJonathan T. Looney
2752529f56eSJonathan T. Looney lockstate = QUEUE_UNLOCKED;
2762529f56eSJonathan T. Looney
2772529f56eSJonathan T. Looney /* Do we need to get a new buffer? */
2782529f56eSJonathan T. Looney while (priv->tldi_cur == NULL ||
2792529f56eSJonathan T. Looney priv->tldi_cur->tlch_length <= priv->tldi_off) {
2802529f56eSJonathan T. Looney /* Did we somehow forget to rotate? */
2812529f56eSJonathan T. Looney KASSERT(priv->tldi_cur == NULL,
2822529f56eSJonathan T. Looney ("%s:%d: tldi_cur is unexpectedly non-NULL", __func__,
2832529f56eSJonathan T. Looney __LINE__));
2842529f56eSJonathan T. Looney if (priv->tldi_cur != NULL)
2852529f56eSJonathan T. Looney tcp_log_dev_rotate_bufs(priv, &lockstate);
2862529f56eSJonathan T. Looney
2872529f56eSJonathan T. Looney /*
2882529f56eSJonathan T. Looney * Before we start looking at tldi_head, we need a lock on the
2892529f56eSJonathan T. Looney * queue to make sure tldi_head stays stable.
2902529f56eSJonathan T. Looney */
2912529f56eSJonathan T. Looney if (lockstate == QUEUE_UNLOCKED) {
2922529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK();
2932529f56eSJonathan T. Looney lockstate = QUEUE_LOCKED;
2942529f56eSJonathan T. Looney }
2952529f56eSJonathan T. Looney
2962529f56eSJonathan T. Looney /* We need the next buffer. Do we have one? */
2972529f56eSJonathan T. Looney if (priv->tldi_head == NULL && (flags & FNONBLOCK)) {
2982529f56eSJonathan T. Looney rv = EAGAIN;
2992529f56eSJonathan T. Looney goto done;
3002529f56eSJonathan T. Looney }
3012529f56eSJonathan T. Looney if (priv->tldi_head == NULL) {
3022529f56eSJonathan T. Looney /* Sleep and wait for more things we can read. */
3032529f56eSJonathan T. Looney rv = mtx_sleep(&tcp_log_dev_listeners,
3042529f56eSJonathan T. Looney &tcp_log_dev_queue_lock, PCATCH, "tcplogdev", 0);
3052529f56eSJonathan T. Looney if (rv)
3062529f56eSJonathan T. Looney goto done;
3072529f56eSJonathan T. Looney if (priv->tldi_head == NULL)
3082529f56eSJonathan T. Looney continue;
3092529f56eSJonathan T. Looney }
3102529f56eSJonathan T. Looney
3112529f56eSJonathan T. Looney /*
3122529f56eSJonathan T. Looney * We have an entry to read. We want to try to create a
3132529f56eSJonathan T. Looney * buffer, if one doesn't already exist.
3142529f56eSJonathan T. Looney */
3152529f56eSJonathan T. Looney entry = priv->tldi_head;
3162529f56eSJonathan T. Looney if (entry->tldq_buf == NULL) {
3172529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK_ASSERT();
3182529f56eSJonathan T. Looney buf = (*entry->tldq_xform)(entry);
3192529f56eSJonathan T. Looney if (buf == NULL) {
3202529f56eSJonathan T. Looney rv = EBUSY;
3212529f56eSJonathan T. Looney goto done;
3222529f56eSJonathan T. Looney }
3232529f56eSJonathan T. Looney entry->tldq_buf = buf;
3242529f56eSJonathan T. Looney }
3252529f56eSJonathan T. Looney
3262529f56eSJonathan T. Looney priv->tldi_cur = entry->tldq_buf;
3272529f56eSJonathan T. Looney priv->tldi_off = 0;
3282529f56eSJonathan T. Looney }
3292529f56eSJonathan T. Looney
3302529f56eSJonathan T. Looney /* Copy what we can from this buffer to the output buffer. */
3312529f56eSJonathan T. Looney if (uio->uio_resid > 0) {
3322529f56eSJonathan T. Looney /* Drop locks so we can take page faults. */
3332529f56eSJonathan T. Looney if (lockstate == QUEUE_LOCKED)
3342529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_UNLOCK();
3352529f56eSJonathan T. Looney lockstate = QUEUE_UNLOCKED;
3362529f56eSJonathan T. Looney
3372529f56eSJonathan T. Looney KASSERT(priv->tldi_cur != NULL,
3382529f56eSJonathan T. Looney ("%s: priv->tldi_cur is unexpectedly NULL", __func__));
3392529f56eSJonathan T. Looney
3402529f56eSJonathan T. Looney /* Copy as much as we can to this uio. */
3412529f56eSJonathan T. Looney len = priv->tldi_cur->tlch_length - priv->tldi_off;
3422529f56eSJonathan T. Looney if (len > uio->uio_resid)
3432529f56eSJonathan T. Looney len = uio->uio_resid;
3442529f56eSJonathan T. Looney rv = uiomove(((uint8_t *)priv->tldi_cur) + priv->tldi_off,
3452529f56eSJonathan T. Looney len, uio);
3462529f56eSJonathan T. Looney if (rv != 0)
3472529f56eSJonathan T. Looney goto done;
3482529f56eSJonathan T. Looney priv->tldi_off += len;
3492529f56eSJonathan T. Looney #ifdef TCPLOG_DEBUG_COUNTERS
3502529f56eSJonathan T. Looney counter_u64_add(tcp_log_que_read, len);
3512529f56eSJonathan T. Looney #endif
3522529f56eSJonathan T. Looney }
3532529f56eSJonathan T. Looney /* Are we done with this buffer? If so, find the next one. */
3542529f56eSJonathan T. Looney if (priv->tldi_off >= priv->tldi_cur->tlch_length) {
3552529f56eSJonathan T. Looney KASSERT(priv->tldi_off == priv->tldi_cur->tlch_length,
3562529f56eSJonathan T. Looney ("%s: offset (%ju) exceeds length (%ju)", __func__,
3572529f56eSJonathan T. Looney (uintmax_t)priv->tldi_off,
3582529f56eSJonathan T. Looney (uintmax_t)priv->tldi_cur->tlch_length));
3592529f56eSJonathan T. Looney tcp_log_dev_rotate_bufs(priv, &lockstate);
3602529f56eSJonathan T. Looney }
3612529f56eSJonathan T. Looney done:
3622529f56eSJonathan T. Looney tcp_log_dev_queue_validate_lock(lockstate);
3632529f56eSJonathan T. Looney if (lockstate == QUEUE_LOCKED)
3642529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_UNLOCK();
3652529f56eSJonathan T. Looney return (rv);
3662529f56eSJonathan T. Looney }
3672529f56eSJonathan T. Looney
3682529f56eSJonathan T. Looney static int
tcp_log_dev_ioctl(struct cdev * dev __unused,u_long cmd,caddr_t data,int fflag __unused,struct thread * td __unused)3692529f56eSJonathan T. Looney tcp_log_dev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
3702529f56eSJonathan T. Looney int fflag __unused, struct thread *td __unused)
3712529f56eSJonathan T. Looney {
3722529f56eSJonathan T. Looney struct tcp_log_dev_info *priv;
3732529f56eSJonathan T. Looney int rv;
3742529f56eSJonathan T. Looney
3752529f56eSJonathan T. Looney /* Get our private info. */
3762529f56eSJonathan T. Looney rv = devfs_get_cdevpriv((void **)&priv);
3772529f56eSJonathan T. Looney if (rv)
3782529f56eSJonathan T. Looney return (rv);
3792529f56eSJonathan T. Looney
3802529f56eSJonathan T. Looney /*
3812529f56eSJonathan T. Looney * Set things. Here, we are most concerned about the non-blocking I/O
3822529f56eSJonathan T. Looney * flag.
3832529f56eSJonathan T. Looney */
3842529f56eSJonathan T. Looney rv = 0;
3852529f56eSJonathan T. Looney switch (cmd) {
3862529f56eSJonathan T. Looney case FIONBIO:
3872529f56eSJonathan T. Looney break;
3882529f56eSJonathan T. Looney case FIOASYNC:
3892529f56eSJonathan T. Looney if (*(int *)data != 0)
3902529f56eSJonathan T. Looney rv = EINVAL;
3912529f56eSJonathan T. Looney break;
3922529f56eSJonathan T. Looney default:
3932529f56eSJonathan T. Looney rv = ENOIOCTL;
3942529f56eSJonathan T. Looney }
3952529f56eSJonathan T. Looney return (rv);
3962529f56eSJonathan T. Looney }
3972529f56eSJonathan T. Looney
3982529f56eSJonathan T. Looney static int
tcp_log_dev_poll(struct cdev * dev __unused,int events,struct thread * td)3992529f56eSJonathan T. Looney tcp_log_dev_poll(struct cdev *dev __unused, int events, struct thread *td)
4002529f56eSJonathan T. Looney {
4012529f56eSJonathan T. Looney struct tcp_log_dev_info *priv;
4022529f56eSJonathan T. Looney int revents;
4032529f56eSJonathan T. Looney
4042529f56eSJonathan T. Looney /*
4052529f56eSJonathan T. Looney * Get our private info. If this fails, claim that all events are
4062529f56eSJonathan T. Looney * ready. That should prod the user to do something that will
4072529f56eSJonathan T. Looney * make the error evident to them.
4082529f56eSJonathan T. Looney */
4092529f56eSJonathan T. Looney if (devfs_get_cdevpriv((void **)&priv))
4102529f56eSJonathan T. Looney return (events);
4112529f56eSJonathan T. Looney
4122529f56eSJonathan T. Looney revents = 0;
4132529f56eSJonathan T. Looney if (events & (POLLIN | POLLRDNORM)) {
4142529f56eSJonathan T. Looney /*
4152529f56eSJonathan T. Looney * We can (probably) read right now if we are partway through
4162529f56eSJonathan T. Looney * a buffer or if we are just about to start a buffer.
4172529f56eSJonathan T. Looney * Because we are going to read tldi_head, we should acquire
4182529f56eSJonathan T. Looney * a read lock on the queue.
4192529f56eSJonathan T. Looney */
4202529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK();
4212529f56eSJonathan T. Looney if ((priv->tldi_head != NULL && priv->tldi_cur == NULL) ||
4222529f56eSJonathan T. Looney (priv->tldi_cur != NULL &&
4232529f56eSJonathan T. Looney priv->tldi_off < priv->tldi_cur->tlch_length))
4242529f56eSJonathan T. Looney revents = events & (POLLIN | POLLRDNORM);
4252529f56eSJonathan T. Looney else
4262529f56eSJonathan T. Looney selrecord(td, &tcp_log_sel);
4272529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_UNLOCK();
4282529f56eSJonathan T. Looney } else {
4292529f56eSJonathan T. Looney /*
4302529f56eSJonathan T. Looney * It only makes sense to poll for reading. So, again, prod the
4312529f56eSJonathan T. Looney * user to do something that will make the error of their ways
4322529f56eSJonathan T. Looney * apparent.
4332529f56eSJonathan T. Looney */
4342529f56eSJonathan T. Looney revents = events;
4352529f56eSJonathan T. Looney }
4362529f56eSJonathan T. Looney return (revents);
4372529f56eSJonathan T. Looney }
4382529f56eSJonathan T. Looney
4392529f56eSJonathan T. Looney int
tcp_log_dev_add_log(struct tcp_log_dev_queue * entry)4402529f56eSJonathan T. Looney tcp_log_dev_add_log(struct tcp_log_dev_queue *entry)
4412529f56eSJonathan T. Looney {
4422529f56eSJonathan T. Looney struct tcp_log_dev_info *priv;
4432529f56eSJonathan T. Looney int rv;
4442529f56eSJonathan T. Looney bool wakeup_needed;
4452529f56eSJonathan T. Looney
4462529f56eSJonathan T. Looney KASSERT(entry->tldq_buf != NULL || entry->tldq_xform != NULL,
4472529f56eSJonathan T. Looney ("%s: Called with both tldq_buf and tldq_xform set to NULL",
4482529f56eSJonathan T. Looney __func__));
4492529f56eSJonathan T. Looney KASSERT(entry->tldq_dtor != NULL,
4502529f56eSJonathan T. Looney ("%s: Called with tldq_dtor set to NULL", __func__));
4512529f56eSJonathan T. Looney
4522529f56eSJonathan T. Looney /* Get a lock on the queue. */
4532529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK();
4542529f56eSJonathan T. Looney
4552529f56eSJonathan T. Looney /* If no one is listening, tell the caller to free the resources. */
4562529f56eSJonathan T. Looney if (tcp_log_dev_listeners == 0) {
4572529f56eSJonathan T. Looney rv = ENXIO;
4582529f56eSJonathan T. Looney goto done;
4592529f56eSJonathan T. Looney }
4602529f56eSJonathan T. Looney
4612529f56eSJonathan T. Looney /* Add this to the end of the tailq. */
4622529f56eSJonathan T. Looney STAILQ_INSERT_TAIL(&tcp_log_dev_queue_head, entry, tldq_queue);
4632529f56eSJonathan T. Looney
4642529f56eSJonathan T. Looney /* Add references for all current listeners. */
4652529f56eSJonathan T. Looney refcount_init(&entry->tldq_refcnt, tcp_log_dev_listeners);
4662529f56eSJonathan T. Looney
4672529f56eSJonathan T. Looney /*
4682529f56eSJonathan T. Looney * If any listener is currently stuck on NULL, that means they are
4692529f56eSJonathan T. Looney * waiting. Point their head to this new entry.
4702529f56eSJonathan T. Looney */
4712529f56eSJonathan T. Looney wakeup_needed = false;
4722529f56eSJonathan T. Looney STAILQ_FOREACH(priv, &tcp_log_dev_reader_head, tldi_list)
4732529f56eSJonathan T. Looney if (priv->tldi_head == NULL) {
4742529f56eSJonathan T. Looney priv->tldi_head = entry;
4752529f56eSJonathan T. Looney wakeup_needed = true;
4762529f56eSJonathan T. Looney }
4772529f56eSJonathan T. Looney
4782529f56eSJonathan T. Looney if (wakeup_needed) {
4792529f56eSJonathan T. Looney selwakeup(&tcp_log_sel);
4802529f56eSJonathan T. Looney wakeup(&tcp_log_dev_listeners);
4812529f56eSJonathan T. Looney }
4822529f56eSJonathan T. Looney
4832529f56eSJonathan T. Looney rv = 0;
4842529f56eSJonathan T. Looney
4852529f56eSJonathan T. Looney done:
4862529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_LOCK_ASSERT();
4872529f56eSJonathan T. Looney TCP_LOG_DEV_QUEUE_UNLOCK();
4882529f56eSJonathan T. Looney return (rv);
4892529f56eSJonathan T. Looney }
4902529f56eSJonathan T. Looney
4912529f56eSJonathan T. Looney static int
tcp_log_dev_modevent(module_t mod __unused,int type,void * data __unused)4922529f56eSJonathan T. Looney tcp_log_dev_modevent(module_t mod __unused, int type, void *data __unused)
4932529f56eSJonathan T. Looney {
4942529f56eSJonathan T. Looney
4952529f56eSJonathan T. Looney /* TODO: Support intelligent unloading. */
4962529f56eSJonathan T. Looney switch (type) {
4972529f56eSJonathan T. Looney case MOD_LOAD:
4982529f56eSJonathan T. Looney if (bootverbose)
4992529f56eSJonathan T. Looney printf("tcp_log: tcp_log device\n");
5002529f56eSJonathan T. Looney memset(&tcp_log_sel, 0, sizeof(tcp_log_sel));
5012529f56eSJonathan T. Looney memset(&tcp_log_dev_queue_lock, 0, sizeof(struct mtx));
5022529f56eSJonathan T. Looney mtx_init(&tcp_log_dev_queue_lock, "tcp_log dev",
5032529f56eSJonathan T. Looney "tcp_log device queues", MTX_DEF);
5042529f56eSJonathan T. Looney tcp_log_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD,
5052529f56eSJonathan T. Looney &tcp_log_cdevsw, 0, NULL, UID_ROOT, GID_WHEEL, 0400,
5062529f56eSJonathan T. Looney "tcp_log");
5072529f56eSJonathan T. Looney break;
5082529f56eSJonathan T. Looney default:
5092529f56eSJonathan T. Looney return (EOPNOTSUPP);
5102529f56eSJonathan T. Looney }
5112529f56eSJonathan T. Looney
5122529f56eSJonathan T. Looney return (0);
5132529f56eSJonathan T. Looney }
5142529f56eSJonathan T. Looney
5152529f56eSJonathan T. Looney DEV_MODULE(tcp_log_dev, tcp_log_dev_modevent, NULL);
5162529f56eSJonathan T. Looney MODULE_VERSION(tcp_log_dev, 1);
517