summaryrefslogtreecommitdiff
path: root/sys/dev/pci/if_mcx.c
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2019-06-10 23:05:53 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2019-06-10 23:05:53 +0000
commita4e6b87242b147086a77909f821a9804088310a1 (patch)
tree1620f22393aee159f96760aa55ea885358f905bb /sys/dev/pci/if_mcx.c
parent36bdb1fbeed42906ae7135d274a45c60c0b3e2ab (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.c125
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)