+void
+in_pcbinit(void)
+{
+ static int inpcb_initialized = 0;
+
+ VERIFY(!inpcb_initialized);
+ inpcb_initialized = 1;
+
+ inpcb_lock_grp_attr = lck_grp_attr_alloc_init();
+ inpcb_lock_grp = lck_grp_alloc_init("inpcb", inpcb_lock_grp_attr);
+ inpcb_lock_attr = lck_attr_alloc_init();
+ lck_mtx_init(&inpcb_lock, inpcb_lock_grp, inpcb_lock_attr);
+ lck_mtx_init(&inpcb_timeout_lock, inpcb_lock_grp, inpcb_lock_attr);
+
+ /*
+ * Initialize data structures required to deliver
+ * flow advisories.
+ */
+ lck_mtx_init(&inp_fc_lck, inpcb_lock_grp, inpcb_lock_attr);
+ lck_mtx_lock(&inp_fc_lck);
+ RB_INIT(&inp_fc_tree);
+ bzero(&key_inp, sizeof(key_inp));
+ lck_mtx_unlock(&inp_fc_lck);
+}
+
+#define INPCB_HAVE_TIMER_REQ(req) (((req).intimer_lazy > 0) || \
+ ((req).intimer_fast > 0) || ((req).intimer_nodelay > 0))
+static void
+inpcb_timeout(void *arg)
+{
+#pragma unused(arg)
+ struct inpcbinfo *ipi;
+ boolean_t t, gc;
+ struct intimercount gccnt, tmcnt;
+ struct timeval leeway;
+ boolean_t toomany_gc = FALSE;
+
+ if (arg != NULL) {
+ VERIFY(arg == &inpcb_toomany_gcreq);
+ toomany_gc = *(boolean_t *)arg;
+ }
+
+ /*
+ * Update coarse-grained networking timestamp (in sec.); the idea
+ * is to piggy-back on the timeout callout to update the counter
+ * returnable via net_uptime().
+ */
+ net_update_uptime();
+
+ bzero(&gccnt, sizeof(gccnt));
+ bzero(&tmcnt, sizeof(tmcnt));
+
+ lck_mtx_lock_spin(&inpcb_timeout_lock);
+ gc = inpcb_garbage_collecting;
+ inpcb_garbage_collecting = FALSE;
+
+ t = inpcb_ticking;
+ inpcb_ticking = FALSE;
+
+ if (gc || t) {
+ lck_mtx_unlock(&inpcb_timeout_lock);
+
+ lck_mtx_lock(&inpcb_lock);
+ TAILQ_FOREACH(ipi, &inpcb_head, ipi_entry) {
+ if (INPCB_HAVE_TIMER_REQ(ipi->ipi_gc_req)) {
+ bzero(&ipi->ipi_gc_req,
+ sizeof(ipi->ipi_gc_req));
+ if (gc && ipi->ipi_gc != NULL) {
+ ipi->ipi_gc(ipi);
+ gccnt.intimer_lazy +=
+ ipi->ipi_gc_req.intimer_lazy;
+ gccnt.intimer_fast +=
+ ipi->ipi_gc_req.intimer_fast;
+ gccnt.intimer_nodelay +=
+ ipi->ipi_gc_req.intimer_nodelay;
+ }
+ }
+ if (INPCB_HAVE_TIMER_REQ(ipi->ipi_timer_req)) {
+ bzero(&ipi->ipi_timer_req,
+ sizeof(ipi->ipi_timer_req));
+ if (t && ipi->ipi_timer != NULL) {
+ ipi->ipi_timer(ipi);
+ tmcnt.intimer_lazy +=
+ ipi->ipi_timer_req.intimer_lazy;
+ tmcnt.intimer_lazy +=
+ ipi->ipi_timer_req.intimer_fast;
+ tmcnt.intimer_nodelay +=
+ ipi->ipi_timer_req.intimer_nodelay;
+ }
+ }
+ }
+ lck_mtx_unlock(&inpcb_lock);
+ lck_mtx_lock_spin(&inpcb_timeout_lock);
+ }
+
+ /* lock was dropped above, so check first before overriding */
+ if (!inpcb_garbage_collecting)
+ inpcb_garbage_collecting = INPCB_HAVE_TIMER_REQ(gccnt);
+ if (!inpcb_ticking)
+ inpcb_ticking = INPCB_HAVE_TIMER_REQ(tmcnt);
+
+ /* re-arm the timer if there's work to do */
+ if (toomany_gc) {
+ inpcb_toomany_gcreq = FALSE;
+ } else {
+ inpcb_timeout_run--;
+ VERIFY(inpcb_timeout_run >= 0 && inpcb_timeout_run < 2);
+ }
+
+ bzero(&leeway, sizeof(leeway));
+ leeway.tv_sec = inpcb_timeout_lazy;
+ if (gccnt.intimer_nodelay > 0 || tmcnt.intimer_nodelay > 0)
+ inpcb_sched_timeout(NULL);
+ else if ((gccnt.intimer_fast + tmcnt.intimer_fast) <= 5)
+ /* be lazy when idle with little activity */
+ inpcb_sched_timeout(&leeway);
+ else
+ inpcb_sched_timeout(NULL);
+
+ lck_mtx_unlock(&inpcb_timeout_lock);
+}
+
+static void
+inpcb_sched_timeout(struct timeval *leeway)
+{
+ lck_mtx_assert(&inpcb_timeout_lock, LCK_MTX_ASSERT_OWNED);
+
+ if (inpcb_timeout_run == 0 &&
+ (inpcb_garbage_collecting || inpcb_ticking)) {
+ lck_mtx_convert_spin(&inpcb_timeout_lock);
+ inpcb_timeout_run++;
+ if (leeway == NULL) {
+ inpcb_fast_timer_on = TRUE;
+ timeout(inpcb_timeout, NULL, hz);
+ } else {
+ inpcb_fast_timer_on = FALSE;
+ timeout_with_leeway(inpcb_timeout, NULL, hz,
+ tvtohz(leeway));
+ }
+ } else if (inpcb_timeout_run == 1 &&
+ leeway == NULL && !inpcb_fast_timer_on) {
+ /*
+ * Since the request was for a fast timer but the
+ * scheduled timer is a lazy timer, try to schedule
+ * another instance of fast timer also
+ */
+ lck_mtx_convert_spin(&inpcb_timeout_lock);
+ inpcb_timeout_run++;
+ inpcb_fast_timer_on = TRUE;
+ timeout(inpcb_timeout, NULL, hz);
+ }
+}
+
+void
+inpcb_gc_sched(struct inpcbinfo *ipi, u_int32_t type)
+{
+ struct timeval leeway;
+ u_int32_t gccnt;
+ lck_mtx_lock_spin(&inpcb_timeout_lock);
+ inpcb_garbage_collecting = TRUE;
+
+ gccnt = ipi->ipi_gc_req.intimer_nodelay +
+ ipi->ipi_gc_req.intimer_fast;
+
+ if (gccnt > INPCB_GCREQ_THRESHOLD && !inpcb_toomany_gcreq) {
+ inpcb_toomany_gcreq = TRUE;
+
+ /*
+ * There are toomany pcbs waiting to be garbage collected,
+ * schedule a much faster timeout in addition to
+ * the caller's request
+ */
+ lck_mtx_convert_spin(&inpcb_timeout_lock);
+ timeout(inpcb_timeout, (void *)&inpcb_toomany_gcreq,
+ INPCB_TOOMANY_GCREQ_TIMER);
+ }
+
+ switch (type) {
+ case INPCB_TIMER_NODELAY:
+ atomic_add_32(&ipi->ipi_gc_req.intimer_nodelay, 1);
+ inpcb_sched_timeout(NULL);
+ break;
+ case INPCB_TIMER_FAST:
+ atomic_add_32(&ipi->ipi_gc_req.intimer_fast, 1);
+ inpcb_sched_timeout(NULL);
+ break;
+ default:
+ atomic_add_32(&ipi->ipi_gc_req.intimer_lazy, 1);
+ leeway.tv_sec = inpcb_timeout_lazy;
+ leeway.tv_usec = 0;
+ inpcb_sched_timeout(&leeway);
+ break;
+ }
+ lck_mtx_unlock(&inpcb_timeout_lock);
+}
+
+void
+inpcb_timer_sched(struct inpcbinfo *ipi, u_int32_t type)
+{
+ struct timeval leeway;
+ lck_mtx_lock_spin(&inpcb_timeout_lock);
+ inpcb_ticking = TRUE;
+ switch (type) {
+ case INPCB_TIMER_NODELAY:
+ atomic_add_32(&ipi->ipi_timer_req.intimer_nodelay, 1);
+ inpcb_sched_timeout(NULL);
+ break;
+ case INPCB_TIMER_FAST:
+ atomic_add_32(&ipi->ipi_timer_req.intimer_fast, 1);
+ inpcb_sched_timeout(NULL);
+ break;
+ default:
+ atomic_add_32(&ipi->ipi_timer_req.intimer_lazy, 1);
+ leeway.tv_sec = inpcb_timeout_lazy;
+ leeway.tv_usec = 0;
+ inpcb_sched_timeout(&leeway);
+ break;
+ }
+ lck_mtx_unlock(&inpcb_timeout_lock);
+}
+
+void
+in_pcbinfo_attach(struct inpcbinfo *ipi)
+{
+ struct inpcbinfo *ipi0;
+
+ lck_mtx_lock(&inpcb_lock);
+ TAILQ_FOREACH(ipi0, &inpcb_head, ipi_entry) {
+ if (ipi0 == ipi) {
+ panic("%s: ipi %p already in the list\n",
+ __func__, ipi);
+ /* NOTREACHED */
+ }
+ }
+ TAILQ_INSERT_TAIL(&inpcb_head, ipi, ipi_entry);
+ lck_mtx_unlock(&inpcb_lock);
+}
+
+int
+in_pcbinfo_detach(struct inpcbinfo *ipi)
+{
+ struct inpcbinfo *ipi0;
+ int error = 0;
+
+ lck_mtx_lock(&inpcb_lock);
+ TAILQ_FOREACH(ipi0, &inpcb_head, ipi_entry) {
+ if (ipi0 == ipi)
+ break;
+ }
+ if (ipi0 != NULL)
+ TAILQ_REMOVE(&inpcb_head, ipi0, ipi_entry);
+ else
+ error = ENXIO;
+ lck_mtx_unlock(&inpcb_lock);
+
+ return (error);
+}
+