NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/60144: virtio(4) cache coherence issue



The following reply was made to PR kern/60144; it has been noted by GNATS.

From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: Jason Thorpe <thorpej%me.com@localhost>
Cc: Tetsuya Isaki <isaki%pastel-flower.jp@localhost>,
	Nick Hudson <nick.hudson%gmx.co.uk@localhost>,
	gnats-bugs%netbsd.org@localhost,
	kern-bug-people%netbsd.org@localhost,
	netbsd-bugs%netbsd.org@localhost
Subject: Re: kern/60144: virtio(4) cache coherence issue
Date: Sat, 11 Apr 2026 15:50:47 +0000

 > Date: Sat, 11 Apr 2026 11:48:32 +0100
 > From: Jason Thorpe <thorpej%me.com@localhost>
 >=20
 > Let me give a quick primer on them just so we're all on the same
 > page regarding now they work.
 
 That's all very interesting, but this is really a question about the
 API rules for bus_dma.
 
 In
 
 	if (buf->ready)
 		use(buf->data);
 
 what bus_dmamap_sync barriers are needed to separate the loads of
 buf->ready and buf->data?  (Similarly if we load buf->ready in a
 polling loop: what barriers are necessary in consecutive loads of
 buf->ready?)
 
 I have long thought that only POSTREAD is necessary here, since we
 already started a DMA transfer into buf a long time ago, with the
 matching PREREAD already issued before it started:
 
 	buf =3D alloc();
 	bus_dmamap_sync(buf, PREREAD);
 	bus_space_write_4(bst, bsh, TRIGGER_DMA, bufaddr);	// start DMA
 	...
 	/* interrupt handler -- buf->ready _may_ have been updated */
 	bus_dmamap_sync(&buf->ready, POSTREAD);
 	if (buf->ready) {
 		/* buf->data _has definitely_ been updated */
 		bus_dmamap_sync(&buf->data, POSTREAD);
 		use(buf->data);
 	}
 
 And I'm pretty sure we have a lot of drivers written like this (most
 of which I did not write!).  For example, here's the wm(4) rx
 interrupt handler (Intel ethernet driver):
 
    10217 static bool
    10218 wm_rxeof(struct wm_rxqueue *rxq, u_int limit)
    10219 {
 ...
    10232 	for (i =3D rxq->rxq_ptr;; i =3D WM_NEXTRX(i)) {
    10233 		rxs =3D &rxq->rxq_soft[i];
 ...
    10238 		wm_cdrxsync(rxq, i,
    10239 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 ...
    10250 		if (!wm_rxdesc_dd(rxq, i, status))
    10251 			break;
 ...
    10277 		bus_dmamap_sync(sc->sc_dmat, rxs->rxs_dmamap, 0,
    10278 		    rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
    10279=20
    10280 		m =3D rxs->rxs_mbuf;
 ...
    10385 		if_percpuq_enqueue(sc->sc_ipq, m);
 
 https://nxr.netbsd.org/xref/src/sys/dev/pci/if_wm.c?r=3D1.801#10212
 
 The call wm_rxdesc_dd corresponds to testing buf->ready, and the
 subsequent use of the content of rxs->rxs_mbuf corresponds to
 use(buf->data).
 
 Here's the mfi(4) interrupt routine (SCSI driver):
 
    1362 int
    1363 mfi_intr(void *arg)
    1364 {
 ...
    1378 	bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_pcq), 0,
    1379 	    sizeof(uint32_t) * sc->sc_max_cmds + sizeof(struct mfi_prod_co=
 ns),
    1380 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
    1381=20
    1382 	producer =3D pcq->mpc_producer;
    1383 	consumer =3D pcq->mpc_consumer;
    1384=20
    1385 	while (consumer !=3D producer) {
 ...
    1389 		ctx =3D pcq->mpc_reply_q[consumer];
 ...
    1397 			ccb =3D &sc->sc_ccb[ctx];
 ...
    1400 			bus_dmamap_sync(sc->sc_dmat, MFIMEM_MAP(sc->sc_frames),
    1401 			    ccb->ccb_pframe - MFIMEM_DVA(sc->sc_frames),
    1402 			    sc->sc_frames_size,
    1403 			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
    1404 			ccb->ccb_done(ccb);
 
 https://nxr.netbsd.org/xref/src/sys/dev/ic/mfi.c?r=3D1.80#1362
 
 Testing pcq->mpc_producer/consumer and loading pcq->mpc_reply_q
 roughly corresponds to testing buf->ready, and the subsequent use of
 ccb corresponding to pcq->mpc_reply_q[consumer] corresponds to using
 buf->data.
 
 The hifn(4) driver (crypto accelerator) does issue another PREREAD,
 but not between testing buf->ready and using buf->data -- only in the
 polling loop of repeatedly testing buf->ready, essentially:
 
    1931 static int
    1932 hifn_intr(void *arg)
    1933 {
 ...
    2000 		HIFN_RESR_SYNC(sc, i,
    2001 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
    2002 		if (dma->resr[i].l & htole32(HIFN_D_VALID)) {
    2003 			HIFN_RESR_SYNC(sc, i,
    2004 			    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
    2005 			break;
    2006 		}
 ...
    2011 			HIFN_RES_SYNC(sc, i, BUS_DMASYNC_POSTREAD);
    2012 			cmd =3D dma->hifn_commands[i];
 ...
    2017 			hifn_callback(sc, cmd, dma->result_bufs[i]);
 
 https://nxr.netbsd.org/xref/src/sys/dev/pci/hifn7751.c#1931
 
 Do we need to go through all these drivers and insert another PREREAD
 between testing buf->ready and using buf->data?  (And between repeated
 tests of buf->ready?)
 
 Note that all of these drivers _already_ issue POSTREAD barriers
 between all the relevant loads.  (There are some drivers that don't do
 even that, and I'm willing to accept that those drivers are simply
 buggy.)
 



Home | Main Index | Thread Index | Old Index