diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2019-06-10 23:05:53 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2019-06-10 23:05:53 +0000 |
commit | a4e6b87242b147086a77909f821a9804088310a1 (patch) | |
tree | 1620f22393aee159f96760aa55ea885358f905bb /sys/dev/pci/if_mcx.c | |
parent | 36bdb1fbeed42906ae7135d274a45c60c0b3e2ab (diff) |
support hardware timestamping for received packets.
the chip has a cycle counter or something, and the value of the
counter when the packet is sent to the host is stored in the
completion queue entry.
the driver periodically checks the cycle counter and records the
time at that point, so the rx path can do some maths to figure out
what the clock time is for the cycle counter. if the driver is
calibrated with the chips counter, the rx packets are timestamped.
this is based on the changes made to freebsd, but with some
simplifications.
Diffstat (limited to 'sys/dev/pci/if_mcx.c')
-rw-r--r-- | sys/dev/pci/if_mcx.c | 125 |
1 files changed, 121 insertions, 4 deletions
diff --git a/sys/dev/pci/if_mcx.c b/sys/dev/pci/if_mcx.c index c1f41ba1210..a09718f67f2 100644 --- a/sys/dev/pci/if_mcx.c +++ b/sys/dev/pci/if_mcx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_mcx.c,v 1.21 2019/06/07 06:53:15 dlg Exp $ */ +/* $OpenBSD: if_mcx.c,v 1.22 2019/06/10 23:05:52 dlg Exp $ */ /* * Copyright (c) 2017 David Gwynne <dlg@openbsd.org> @@ -1892,6 +1892,18 @@ struct mcx_cq { int cq_count; }; +struct mcx_calibration { + uint64_t c_timestamp; /* previous mcx chip time */ + uint64_t c_uptime; /* previous kernel nanouptime */ + uint64_t c_tbase; /* mcx chip time */ + uint64_t c_ubase; /* kernel nanouptime */ + uint64_t c_tdiff; + uint64_t c_udiff; +}; + +#define MCX_CALIBRATE_FIRST 2 +#define MCX_CALIBRATE_NORMAL 30 + struct mcx_softc { struct device sc_dev; struct arpcom sc_ac; @@ -1945,6 +1957,10 @@ struct mcx_softc { int sc_extra_mcast; uint8_t sc_mcast_flows[MCX_NUM_MCAST_FLOWS][ETHER_ADDR_LEN]; + struct mcx_calibration sc_calibration[2]; + unsigned int sc_calibration_gen; + struct timeout sc_calibrate; + struct mcx_cq sc_cq[MCX_MAX_CQS]; int sc_num_cq; @@ -2037,7 +2053,7 @@ static void mcx_cmdq_mbox_dump(struct mcx_dmamem *, int); */ static void mcx_refill(void *); static int mcx_process_rx(struct mcx_softc *, struct mcx_cq_entry *, - struct mbuf_list *); + struct mbuf_list *, const struct mcx_calibration *); static void mcx_process_txeof(struct mcx_softc *, struct mcx_cq_entry *, int *); static void mcx_process_cq(struct mcx_softc *, struct mcx_cq *); @@ -2057,6 +2073,9 @@ static void mcx_media_status(struct ifnet *, struct ifmediareq *); static int mcx_media_change(struct ifnet *); static int mcx_get_sffpage(struct ifnet *, struct if_sffpage *); +static void mcx_calibrate_first(struct mcx_softc *); +static void mcx_calibrate(void *); + static inline uint32_t mcx_rd(struct mcx_softc *, bus_size_t); static inline void @@ -2064,6 +2083,8 @@ static inline void static inline void mcx_bar(struct mcx_softc *, bus_size_t, bus_size_t, int); +static uint64_t mcx_timer(struct mcx_softc *); + static int mcx_dmamem_alloc(struct mcx_softc *, struct mcx_dmamem *, bus_size_t, u_int align); static void mcx_dmamem_zero(struct mcx_dmamem *); @@ -2338,6 +2359,7 @@ mcx_attach(struct device *parent, struct device *self, void *aux) ether_ifattach(ifp); timeout_set(&sc->sc_rx_refill, mcx_refill, sc); + timeout_set(&sc->sc_calibrate, mcx_calibrate, sc); sc->sc_flow_table_id = -1; for (i = 0; i < MCX_NUM_FLOW_GROUPS; i++) { @@ -5555,9 +5577,65 @@ mcx_process_txeof(struct mcx_softc *sc, struct mcx_cq_entry *cqe, int *txfree) ms->ms_m = NULL; } +static uint64_t +mcx_uptime(void) +{ + struct timespec ts; + + nanouptime(&ts); + + return ((uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec); +} + +static void +mcx_calibrate_first(struct mcx_softc *sc) +{ + struct mcx_calibration *c = &sc->sc_calibration[0]; + + sc->sc_calibration_gen = 0; + + c->c_ubase = mcx_uptime(); + c->c_tbase = mcx_timer(sc); + c->c_tdiff = 0; + + timeout_add_sec(&sc->sc_calibrate, MCX_CALIBRATE_FIRST); +} + +#define MCX_TIMESTAMP_SHIFT 10 + +static void +mcx_calibrate(void *arg) +{ + struct mcx_softc *sc = arg; + struct mcx_calibration *nc, *pc; + unsigned int gen; + + if (!ISSET(sc->sc_ac.ac_if.if_flags, IFF_RUNNING)) + return; + + timeout_add_sec(&sc->sc_calibrate, MCX_CALIBRATE_NORMAL); + + gen = sc->sc_calibration_gen; + pc = &sc->sc_calibration[gen % nitems(sc->sc_calibration)]; + gen++; + nc = &sc->sc_calibration[gen % nitems(sc->sc_calibration)]; + + nc->c_uptime = pc->c_ubase; + nc->c_timestamp = pc->c_tbase; + + nc->c_ubase = mcx_uptime(); + nc->c_tbase = mcx_timer(sc); + + nc->c_udiff = (nc->c_ubase - nc->c_uptime) >> MCX_TIMESTAMP_SHIFT; + nc->c_tdiff = (nc->c_tbase - nc->c_timestamp) >> MCX_TIMESTAMP_SHIFT; + + membar_producer(); + sc->sc_calibration_gen = gen; +} + static int mcx_process_rx(struct mcx_softc *sc, struct mcx_cq_entry *cqe, - struct mbuf_list *ml) + struct mbuf_list *ml, const struct mcx_calibration *c) { struct mcx_slot *ms; struct mbuf *m; @@ -5575,6 +5653,15 @@ mcx_process_rx(struct mcx_softc *sc, struct mcx_cq_entry *cqe, m->m_pkthdr.len = m->m_len = bemtoh32(&cqe->cq_byte_cnt); + if (c->c_tdiff) { + uint64_t t = bemtoh64(&cqe->cq_timestamp) - c->c_timestamp; + t *= c->c_udiff; + t /= c->c_tdiff; + + m->m_pkthdr.ph_timestamp = c->c_uptime + t; + SET(m->m_pkthdr.csum_flags, M_TIMESTAMP); + } + ml_enqueue(ml, m); return (1); @@ -5624,11 +5711,17 @@ void mcx_process_cq(struct mcx_softc *sc, struct mcx_cq *cq) { struct ifnet *ifp = &sc->sc_ac.ac_if; + const struct mcx_calibration *c; + unsigned int gen; struct mcx_cq_entry *cqe; uint8_t *cqp; struct mbuf_list ml = MBUF_LIST_INITIALIZER(); int rxfree, txfree; + gen = sc->sc_calibration_gen; + membar_consumer(); + c = &sc->sc_calibration[gen % nitems(sc->sc_calibration)]; + rxfree = 0; txfree = 0; while ((cqe = mcx_next_cq_entry(sc, cq))) { @@ -5639,7 +5732,7 @@ mcx_process_cq(struct mcx_softc *sc, struct mcx_cq *cq) mcx_process_txeof(sc, cqe, &txfree); break; case MCX_CQ_ENTRY_OPCODE_SEND: - rxfree += mcx_process_rx(sc, cqe, &ml); + rxfree += mcx_process_rx(sc, cqe, &ml, c); break; case MCX_CQ_ENTRY_OPCODE_REQ_ERR: case MCX_CQ_ENTRY_OPCODE_SEND_ERR: @@ -5882,6 +5975,8 @@ mcx_up(struct mcx_softc *sc) sc->sc_rx_prod = 0; mcx_rx_fill(sc); + mcx_calibrate_first(sc); + SET(ifp->if_flags, IFF_RUNNING); sc->sc_tx_cons = 0; @@ -5930,6 +6025,8 @@ mcx_down(struct mcx_softc *sc) intr_barrier(&sc->sc_ih); ifq_barrier(&ifp->if_snd); + timeout_del_barrier(&sc->sc_calibrate); + for (group = 0; group < MCX_NUM_FLOW_GROUPS; group++) { if (sc->sc_flow_group_id[group] != -1) mcx_destroy_flow_group(sc, @@ -6445,6 +6542,26 @@ mcx_bar(struct mcx_softc *sc, bus_size_t r, bus_size_t l, int f) bus_space_barrier(sc->sc_memt, sc->sc_memh, r, l, f); } +static uint64_t +mcx_timer(struct mcx_softc *sc) +{ + uint32_t hi, lo, ni; + + hi = mcx_rd(sc, MCX_INTERNAL_TIMER_H); + for (;;) { + lo = mcx_rd(sc, MCX_INTERNAL_TIMER_L); + mcx_bar(sc, MCX_INTERNAL_TIMER_L, 8, BUS_SPACE_BARRIER_READ); + ni = mcx_rd(sc, MCX_INTERNAL_TIMER_H); + + if (ni == hi) + break; + + hi = ni; + } + + return (((uint64_t)hi << 32) | (uint64_t)lo); +} + static int mcx_dmamem_alloc(struct mcx_softc *sc, struct mcx_dmamem *mxm, bus_size_t size, u_int align) |