+/*
+ * Pre-enqueue callback.
+ *
+ * This routine is called only when lo_txstart is enabled.
+ */
+static errno_t
+lo_pre_enqueue(struct ifnet *ifp, struct mbuf *m0)
+{
+ struct mbuf *m = m0, *n;
+ int error = 0;
+
+ while (m != NULL) {
+ VERIFY(m->m_flags & M_PKTHDR);
+
+ n = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+
+ /*
+ * Don't overwrite the rcvif field if it is in use.
+ * This is used to match multicast packets, sent looping
+ * back, with the appropriate group record on input.
+ */
+ if (m->m_pkthdr.rcvif == NULL)
+ m->m_pkthdr.rcvif = ifp;
+
+ m->m_pkthdr.pkt_flags |= PKTF_LOOP;
+ m->m_pkthdr.pkt_hdr = mtod(m, char *);
+
+ /* loopback checksums are always OK */
+ m->m_pkthdr.csum_data = 0xffff;
+ m->m_pkthdr.csum_flags =
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
+ CSUM_IP_CHECKED | CSUM_IP_VALID;
+
+ m_adj(m, sizeof (struct loopback_header));
+
+ /*
+ * Let the callee free it in case of error,
+ * and perform any necessary accounting.
+ */
+ (void) ifnet_enqueue(ifp, m);
+
+ m = n;
+ }
+
+ return (error);
+}
+
+/*
+ * Start output callback.
+ *
+ * This routine is invoked by the start worker thread; because we never call
+ * it directly, there is no need do deploy any serialization mechanism other
+ * than what's already used by the worker thread, i.e. this is already single
+ * threaded.
+ *
+ * This routine is called only when lo_txstart is enabled.
+ */
+static void
+lo_start(struct ifnet *ifp)
+{
+ struct ifnet_stat_increment_param s;
+
+ bzero(&s, sizeof (s));
+
+ for (;;) {
+ struct mbuf *m = NULL, *m_tail = NULL;
+ u_int32_t cnt, len = 0;
+
+ if (lo_sched_model == IFNET_SCHED_MODEL_NORMAL) {
+ if (ifnet_dequeue_multi(ifp, lo_dequeue_max, &m,
+ &m_tail, &cnt, &len) != 0)
+ break;
+ } else {
+ if (ifnet_dequeue_service_class_multi(ifp,
+ lo_dequeue_sc, lo_dequeue_max, &m,
+ &m_tail, &cnt, &len) != 0)
+ break;
+ }
+
+ LO_BPF_TAP_OUT_MULTI(m);
+ lo_tx_compl(ifp, m);
+
+ /* stats are required for extended variant */
+ s.packets_in = cnt;
+ s.packets_out = cnt;
+ s.bytes_in = len;
+ s.bytes_out = len;
+
+ (void) ifnet_input_extended(ifp, m, m_tail, &s);
+ }
+}