Date: Mon, 7 Jan 2019 04:23:06 +0000 (UTC) From: Andriy Voskoboinyk <avos@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r342830 - in stable/12/sys: dev/rtwn/pci dev/rtwn/rtl8188e dev/rtwn/rtl8188e/usb dev/rtwn/rtl8192c dev/rtwn/rtl8192c/pci dev/rtwn/rtl8192c/usb modules/rtwn_usb Message-ID: <201901070423.x074N6t7037711@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: avos Date: Mon Jan 7 04:23:06 2019 New Revision: 342830 URL: https://svnweb.freebsd.org/changeset/base/342830 Log: MFC r342677: rtwn_pci(4): add support for event-based Tx reports. It will be used for RTL8188EE (and, probably, others). MFC r342700: rtwn_pci(4): fix panic with INVARIANTS (due to inverted assertion logic) Deleted: stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu_rx.c Modified: stable/12/sys/dev/rtwn/pci/rtwn_pci_rx.c stable/12/sys/dev/rtwn/pci/rtwn_pci_var.h stable/12/sys/dev/rtwn/rtl8188e/r88e.h stable/12/sys/dev/rtwn/rtl8188e/r88e_rx.c stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu.h stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce.h stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_rx.c stable/12/sys/dev/rtwn/rtl8192c/r92c.h stable/12/sys/dev/rtwn/rtl8192c/r92c_rx.c stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu.h stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_rx.c stable/12/sys/modules/rtwn_usb/Makefile Directory Properties: stable/12/ (props changed) Modified: stable/12/sys/dev/rtwn/pci/rtwn_pci_rx.c ============================================================================== --- stable/12/sys/dev/rtwn/pci/rtwn_pci_rx.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/pci/rtwn_pci_rx.c Mon Jan 7 04:23:06 2019 (r342830) @@ -83,12 +83,12 @@ rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *pc, } static void -rtwn_pci_rx_frame(struct rtwn_softc *sc, struct rtwn_rx_stat_pci *rx_desc, - int desc_idx) +rtwn_pci_rx_frame(struct rtwn_pci_softc *pc) { - struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); + struct rtwn_softc *sc = &pc->pc_sc; struct rtwn_rx_ring *ring = &pc->rx_ring; - struct rtwn_rx_data *rx_data = &ring->rx_data[desc_idx]; + struct rtwn_rx_stat_pci *rx_desc = &ring->desc[ring->cur]; + struct rtwn_rx_data *rx_data = &ring->rx_data[ring->cur]; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; uint32_t rxdw0; @@ -148,9 +148,6 @@ rtwn_pci_rx_frame(struct rtwn_softc *sc, struct rtwn_r panic("%s: could not load old RX mbuf", device_get_name(sc->sc_dev)); - /* Physical address may have changed. */ - rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, - MJUMPAGESIZE, desc_idx); goto fail; } @@ -165,10 +162,6 @@ rtwn_pci_rx_frame(struct rtwn_softc *sc, struct rtwn_r "%s: Rx frame len %d, infosz %d, shift %d\n", __func__, pktlen, infosz, shift); - /* Update RX descriptor. */ - rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, MJUMPAGESIZE, - desc_idx); - /* Send the frame to the 802.11 layer. */ RTWN_UNLOCK(sc); if (ni != NULL) { @@ -186,7 +179,73 @@ fail: counter_u64_add(ic->ic_ierrors, 1); } +static int +rtwn_pci_rx_buf_copy(struct rtwn_pci_softc *pc) +{ + struct rtwn_rx_ring *ring = &pc->rx_ring; + struct rtwn_rx_stat_pci *rx_desc = &ring->desc[ring->cur]; + struct rtwn_rx_data *rx_data = &ring->rx_data[ring->cur]; + uint32_t rxdw0; + int desc_size, pktlen; + + /* + * NB: tx_report() / c2h_report() expects to see USB Rx + * descriptor - same as for PCIe, but without rxbufaddr* fields. + */ + desc_size = sizeof(struct rtwn_rx_stat_common); + KASSERT(sizeof(pc->pc_rx_buf) >= desc_size, + ("adjust size for PCIe Rx buffer!")); + + memcpy(pc->pc_rx_buf, rx_desc, desc_size); + + rxdw0 = le32toh(rx_desc->rxdw0); + pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); + + if (pktlen > sizeof(pc->pc_rx_buf) - desc_size) + { + /* Looks like an ordinary Rx frame. */ + return (desc_size); + } + + bus_dmamap_sync(ring->data_dmat, rx_data->map, BUS_DMASYNC_POSTREAD); + memcpy(pc->pc_rx_buf + desc_size, mtod(rx_data->m, void *), pktlen); + + return (desc_size + pktlen); +} + static void +rtwn_pci_tx_report(struct rtwn_pci_softc *pc, int len) +{ + struct rtwn_softc *sc = &pc->pc_sc; + + if (sc->sc_ratectl != RTWN_RATECTL_NET80211) { + /* shouldn't happen */ + device_printf(sc->sc_dev, + "%s called while ratectl = %d!\n", + __func__, sc->sc_ratectl); + return; + } + + RTWN_NT_LOCK(sc); + rtwn_handle_tx_report(sc, pc->pc_rx_buf, len); + RTWN_NT_UNLOCK(sc); + +#ifdef IEEE80211_SUPPORT_SUPERG + /* + * NB: this will executed only when 'report' bit is set. + */ + if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1) + rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); +#endif +} + +static void +rtwn_pci_c2h_report(struct rtwn_pci_softc *pc, int len) +{ + rtwn_handle_c2h_report(&pc->pc_sc, pc->pc_rx_buf, len); +} + +static void rtwn_pci_tx_done(struct rtwn_softc *sc, int qid) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); @@ -263,21 +322,50 @@ rtwn_pci_rx_done(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *ring = &pc->rx_ring; + struct rtwn_rx_stat_pci *rx_desc; + struct rtwn_rx_data *rx_data; + int len; bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); for (;;) { - struct rtwn_rx_stat_pci *rx_desc = &ring->desc[ring->cur]; + rx_desc = &ring->desc[ring->cur]; + rx_data = &ring->rx_data[ring->cur]; if (le32toh(rx_desc->rxdw0) & RTWN_RXDW0_OWN) break; - rtwn_pci_rx_frame(sc, rx_desc, ring->cur); + len = rtwn_pci_rx_buf_copy(pc); + switch (rtwn_classify_intr(sc, pc->pc_rx_buf, len)) { + case RTWN_RX_DATA: + rtwn_pci_rx_frame(pc); + break; + case RTWN_RX_TX_REPORT: + rtwn_pci_tx_report(pc, len); + break; + case RTWN_RX_OTHER: + rtwn_pci_c2h_report(pc, len); + break; + default: + /* NOTREACHED */ + KASSERT(0, ("unknown Rx classification code")); + break; + } + + /* Update / reset RX descriptor (and set OWN bit). */ + rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, + MJUMPAGESIZE, ring->cur); + if (!(sc->sc_flags & RTWN_RUNNING)) return; - ring->cur = (ring->cur + 1) % RTWN_PCI_RX_LIST_COUNT; + /* NB: device can reuse current descriptor. */ + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, + BUS_DMASYNC_POSTREAD); + + if (le32toh(rx_desc->rxdw0) & RTWN_RXDW0_OWN) + ring->cur = (ring->cur + 1) % RTWN_PCI_RX_LIST_COUNT; } } @@ -289,13 +377,13 @@ rtwn_pci_intr(void *arg) int i, status, tx_rings; RTWN_LOCK(sc); - status = rtwn_classify_intr(sc, &tx_rings, 0); + status = rtwn_pci_get_intr_status(pc, &tx_rings); RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: status %08X, tx_rings %08X\n", __func__, status, tx_rings); if (status == 0 && tx_rings == 0) goto unlock; - if (status & RTWN_PCI_INTR_RX) { + if (status & (RTWN_PCI_INTR_RX | RTWN_PCI_INTR_TX_REPORT)) { rtwn_pci_rx_done(sc); if (!(sc->sc_flags & RTWN_RUNNING)) goto unlock; Modified: stable/12/sys/dev/rtwn/pci/rtwn_pci_var.h ============================================================================== --- stable/12/sys/dev/rtwn/pci/rtwn_pci_var.h Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/pci/rtwn_pci_var.h Mon Jan 7 04:23:06 2019 (r342830) @@ -26,6 +26,9 @@ #define RTWN_PCI_RX_LIST_COUNT 256 #define RTWN_PCI_TX_LIST_COUNT 256 +/* sizeof(struct rtwn_rx_stat_common) + R88E_INTR_MSG_LEN */ +#define RTWN_PCI_RX_TMP_BUF_SIZE 84 + struct rtwn_rx_data { bus_dmamap_t map; struct mbuf *m; @@ -95,8 +98,8 @@ enum { /* Shortcuts */ /* Vendor driver treats RX errors like ROK... */ #define RTWN_PCI_INTR_RX \ - (RTWN_PCI_INTR_RX_OVERFLOW | RTWN_PCI_INTR_RX_DESC_UNAVAIL | \ - RTWN_PCI_INTR_RX_DONE) + (RTWN_PCI_INTR_RX_ERROR | RTWN_PCI_INTR_RX_OVERFLOW | \ + RTWN_PCI_INTR_RX_DESC_UNAVAIL | RTWN_PCI_INTR_RX_DONE) struct rtwn_pci_softc { @@ -109,6 +112,7 @@ struct rtwn_pci_softc { void *pc_ih; bus_size_t pc_mapsize; + uint8_t pc_rx_buf[RTWN_PCI_RX_TMP_BUF_SIZE]; struct rtwn_rx_ring rx_ring; struct rtwn_tx_ring tx_ring[RTWN_PCI_NTXQUEUES]; @@ -122,6 +126,8 @@ struct rtwn_pci_softc { void *, bus_dma_segment_t *); void (*pc_copy_tx_desc)(void *, const void *); void (*pc_enable_intr)(struct rtwn_pci_softc *); + int (*pc_get_intr_status)(struct rtwn_pci_softc *, + int *); }; #define RTWN_PCI_SOFTC(sc) ((struct rtwn_pci_softc *)(sc)) @@ -133,5 +139,7 @@ struct rtwn_pci_softc { (((_pc)->pc_copy_tx_desc)((_dest), (_src))) #define rtwn_pci_enable_intr(_pc) \ (((_pc)->pc_enable_intr)((_pc))) +#define rtwn_pci_get_intr_status(_pc, _tx_rings) \ + (((_pc)->pc_get_intr_status)((_pc), (_tx_rings))) #endif /* RTWN_PCI_VAR_H */ Modified: stable/12/sys/dev/rtwn/rtl8188e/r88e.h ============================================================================== --- stable/12/sys/dev/rtwn/rtl8188e/r88e.h Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8188e/r88e.h Mon Jan 7 04:23:06 2019 (r342830) @@ -81,6 +81,7 @@ void r88e_rf_write(struct rtwn_softc *, int, uint8_t, void r88e_parse_rom(struct rtwn_softc *, uint8_t *); /* r88e_rx.c */ +int r88e_classify_intr(struct rtwn_softc *, void *, int); void r88e_ratectl_tx_complete(struct rtwn_softc *, uint8_t *, int); void r88e_handle_c2h_report(struct rtwn_softc *, uint8_t *, int); int8_t r88e_get_rssi_cck(struct rtwn_softc *, void *); Modified: stable/12/sys/dev/rtwn/rtl8188e/r88e_rx.c ============================================================================== --- stable/12/sys/dev/rtwn/rtl8188e/r88e_rx.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8188e/r88e_rx.c Mon Jan 7 04:23:06 2019 (r342830) @@ -56,6 +56,25 @@ __FBSDID("$FreeBSD$"); #include <dev/rtwn/rtl8188e/r88e_rx_desc.h> +int +r88e_classify_intr(struct rtwn_softc *sc, void *buf, int len) +{ + struct r92c_rx_stat *stat = buf; + int report_sel = MS(le32toh(stat->rxdw3), R88E_RXDW3_RPT); + + switch (report_sel) { + case R88E_RXDW3_RPT_RX: + return (RTWN_RX_DATA); + case R88E_RXDW3_RPT_TX1: /* per-packet Tx report */ + case R88E_RXDW3_RPT_TX2: /* periodical Tx report */ + return (RTWN_RX_TX_REPORT); + case R88E_RXDW3_RPT_HIS: + return (RTWN_RX_OTHER); + default: /* shut up the compiler */ + return (RTWN_RX_DATA); + } +} + void r88e_ratectl_tx_complete(struct rtwn_softc *sc, uint8_t *buf, int len) { Modified: stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu.h ============================================================================== --- stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu.h Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu.h Mon Jan 7 04:23:06 2019 (r342830) @@ -33,7 +33,4 @@ void r88eu_init_intr(struct rtwn_softc *); void r88eu_init_rx_agg(struct rtwn_softc *); void r88eu_post_init(struct rtwn_softc *); -/* r88eu_rx.c */ -int r88eu_classify_intr(struct rtwn_softc *, void *, int); - #endif /* RTL8188EU_H */ Modified: stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c ============================================================================== --- stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8188e/usb/r88eu_attach.c Mon Jan 7 04:23:06 2019 (r342830) @@ -130,7 +130,7 @@ r88eu_attach(struct rtwn_usb_softc *uc) sc->sc_get_rx_stats = r88e_get_rx_stats; sc->sc_get_rssi_cck = r88e_get_rssi_cck; sc->sc_get_rssi_ofdm = r88e_get_rssi_ofdm; - sc->sc_classify_intr = r88eu_classify_intr; + sc->sc_classify_intr = r88e_classify_intr; sc->sc_handle_tx_report = r88e_ratectl_tx_complete; sc->sc_handle_c2h_report = r88e_handle_c2h_report; sc->sc_check_frame = rtwn_nop_int_softc_mbuf; Modified: stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce.h ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce.h Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce.h Mon Jan 7 04:23:06 2019 (r342830) @@ -60,7 +60,7 @@ void r92ce_post_init(struct rtwn_softc *); void r92ce_set_led(struct rtwn_softc *, int, int); /* r92ce_rx.c */ -int r92ce_classify_intr(struct rtwn_softc *, void *, int); +int r92ce_get_intr_status(struct rtwn_pci_softc *, int *); void r92ce_enable_intr(struct rtwn_pci_softc *); void r92ce_start_xfers(struct rtwn_softc *); Modified: stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_attach.c Mon Jan 7 04:23:06 2019 (r342830) @@ -155,6 +155,7 @@ r92ce_attach(struct rtwn_pci_softc *pc) pc->pc_tx_postsetup = r92ce_tx_postsetup; pc->pc_copy_tx_desc = r92ce_copy_tx_desc; pc->pc_enable_intr = r92ce_enable_intr; + pc->pc_get_intr_status = r92ce_get_intr_status; pc->pc_qmap = 0xf771; pc->tcr = @@ -175,7 +176,7 @@ r92ce_attach(struct rtwn_pci_softc *pc) sc->sc_get_rx_stats = r92c_get_rx_stats; sc->sc_get_rssi_cck = r92c_get_rssi_cck; sc->sc_get_rssi_ofdm = r92c_get_rssi_ofdm; - sc->sc_classify_intr = r92ce_classify_intr; + sc->sc_classify_intr = r92c_classify_intr; sc->sc_handle_tx_report = rtwn_nop_softc_uint8_int; sc->sc_handle_c2h_report = rtwn_nop_softc_uint8_int; sc->sc_check_frame = rtwn_nop_int_softc_mbuf; Modified: stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_rx.c ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_rx.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/pci/r92ce_rx.c Mon Jan 7 04:23:06 2019 (r342830) @@ -58,10 +58,10 @@ __FBSDID("$FreeBSD$"); int -r92ce_classify_intr(struct rtwn_softc *sc, void *arg, int len __unused) +r92ce_get_intr_status(struct rtwn_pci_softc *pc, int *rings) { + struct rtwn_softc *sc = &pc->pc_sc; uint32_t status; - int *rings = arg; int ret; *rings = 0; Modified: stable/12/sys/dev/rtwn/rtl8192c/r92c.h ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/r92c.h Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/r92c.h Mon Jan 7 04:23:06 2019 (r342830) @@ -102,6 +102,7 @@ void r92c_efuse_postread(struct rtwn_softc *); void r92c_parse_rom(struct rtwn_softc *, uint8_t *); /* r92c_rx.c */ +int r92c_classify_intr(struct rtwn_softc *, void *, int); int8_t r92c_get_rssi_cck(struct rtwn_softc *, void *); int8_t r92c_get_rssi_ofdm(struct rtwn_softc *, void *); uint8_t r92c_rx_radiotap_flags(const void *); Modified: stable/12/sys/dev/rtwn/rtl8192c/r92c_rx.c ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/r92c_rx.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/r92c_rx.c Mon Jan 7 04:23:06 2019 (r342830) @@ -52,6 +52,13 @@ __FBSDID("$FreeBSD$"); #include <dev/rtwn/rtl8192c/r92c_rx_desc.h> +int +r92c_classify_intr(struct rtwn_softc *sc, void *buf, int len) +{ + /* NB: reports are fetched from C2H_MSG register. */ + return (RTWN_RX_DATA); +} + int8_t r92c_get_rssi_cck(struct rtwn_softc *sc, void *physt) { Modified: stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu.h ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu.h Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu.h Mon Jan 7 04:23:06 2019 (r342830) @@ -47,7 +47,6 @@ void r92cu_post_init(struct rtwn_softc *); void r92cu_set_led(struct rtwn_softc *, int, int); /* r92cu_rx.c */ -int r92cu_classify_intr(struct rtwn_softc *, void *, int); int r92cu_align_rx(int, int); /* r92cu_tx.c */ Modified: stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_attach.c Mon Jan 7 04:23:06 2019 (r342830) @@ -168,7 +168,7 @@ r92cu_attach(struct rtwn_usb_softc *uc) sc->sc_get_rx_stats = r92c_get_rx_stats; sc->sc_get_rssi_cck = r92c_get_rssi_cck; sc->sc_get_rssi_ofdm = r92c_get_rssi_ofdm; - sc->sc_classify_intr = r92cu_classify_intr; + sc->sc_classify_intr = r92c_classify_intr; sc->sc_handle_tx_report = rtwn_nop_softc_uint8_int; sc->sc_handle_c2h_report = rtwn_nop_softc_uint8_int; sc->sc_check_frame = rtwn_nop_int_softc_mbuf; Modified: stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_rx.c ============================================================================== --- stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_rx.c Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/dev/rtwn/rtl8192c/usb/r92cu_rx.c Mon Jan 7 04:23:06 2019 (r342830) @@ -50,13 +50,6 @@ __FBSDID("$FreeBSD$"); int -r92cu_classify_intr(struct rtwn_softc *sc, void *buf, int len) -{ - /* NB: reports are fetched from C2H_MSG register. */ - return (RTWN_RX_DATA); -} - -int r92cu_align_rx(int totlen, int len) { return (roundup2(totlen, 128)); Modified: stable/12/sys/modules/rtwn_usb/Makefile ============================================================================== --- stable/12/sys/modules/rtwn_usb/Makefile Mon Jan 7 04:10:54 2019 (r342829) +++ stable/12/sys/modules/rtwn_usb/Makefile Mon Jan 7 04:23:06 2019 (r342830) @@ -13,7 +13,7 @@ SRCS = rtwn_usb_attach.c rtwn_usb_ep.c rtwn_usb_reg.c opt_bus.h opt_rtwn.h opt_usb.h opt_wlan.h usb_if.h usbdevs.h .PATH: ${SRCTOP}/sys/dev/rtwn/rtl8188e/usb -SRCS += r88eu_attach.c r88eu_init.c r88eu_rx.c \ +SRCS += r88eu_attach.c r88eu_init.c \ r88eu.h r88eu_reg.h .PATH: ${SRCTOP}/sys/dev/rtwn/rtl8192c/usb
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201901070423.x074N6t7037711>