summaryrefslogtreecommitdiff
path: root/sys/dev/pci/if_myx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/if_myx.c')
-rw-r--r--sys/dev/pci/if_myx.c207
1 files changed, 153 insertions, 54 deletions
diff --git a/sys/dev/pci/if_myx.c b/sys/dev/pci/if_myx.c
index 3e4380a6f1b..ae9105eec20 100644
--- a/sys/dev/pci/if_myx.c
+++ b/sys/dev/pci/if_myx.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_myx.c,v 1.49 2014/01/19 21:43:36 dlg Exp $ */
+/* $OpenBSD: if_myx.c,v 1.50 2014/01/21 23:26:50 dlg Exp $ */
/*
* Copyright (c) 2007 Reyk Floeter <reyk@openbsd.org>
@@ -94,6 +94,11 @@ struct myx_buf_list {
struct pool *myx_buf_pool;
+struct myx_ring_lock {
+ struct mutex mrl_mtx;
+ u_int mrl_running;
+};
+
struct myx_softc {
struct device sc_dev;
struct arpcom sc_ac;
@@ -114,6 +119,7 @@ struct myx_softc {
struct myx_dmamem sc_sts_dma;
volatile struct myx_status *sc_sts;
+ struct mutex sc_sts_mtx;
int sc_intx;
void *sc_irqh;
@@ -127,6 +133,7 @@ struct myx_softc {
u_int sc_intrq_idx;
u_int sc_rx_ring_count;
+ struct myx_ring_lock sc_rx_ring_lock[2];
u_int32_t sc_rx_ring_offset[2];
struct myx_buf_list sc_rx_buf_free[2];
struct myx_buf_list sc_rx_buf_list[2];
@@ -137,6 +144,7 @@ struct myx_softc {
bus_size_t sc_tx_boundary;
u_int sc_tx_ring_count;
+ struct myx_ring_lock sc_tx_ring_lock;
u_int32_t sc_tx_ring_offset;
u_int sc_tx_nsegs;
u_int32_t sc_tx_count; /* shadows ms_txdonecnt */
@@ -179,7 +187,7 @@ int myx_dmamem_alloc(struct myx_softc *, struct myx_dmamem *,
void myx_dmamem_free(struct myx_softc *, struct myx_dmamem *);
int myx_media_change(struct ifnet *);
void myx_media_status(struct ifnet *, struct ifmediareq *);
-void myx_link_state(struct myx_softc *);
+void myx_link_state(struct myx_softc *, u_int32_t);
void myx_watchdog(struct ifnet *);
int myx_ioctl(struct ifnet *, u_long, caddr_t);
void myx_up(struct myx_softc *);
@@ -208,6 +216,10 @@ void myx_rx_zero(struct myx_softc *, int);
int myx_rx_fill(struct myx_softc *, int);
void myx_refill(void *);
+void myx_ring_lock_init(struct myx_ring_lock *);
+int myx_ring_enter(struct myx_ring_lock *);
+int myx_ring_leave(struct myx_ring_lock *);
+
struct cfdriver myx_cd = {
NULL, "myx", DV_IFNET
};
@@ -239,16 +251,22 @@ myx_attach(struct device *parent, struct device *self, void *aux)
sc->sc_dmat = pa->pa_dmat;
sc->sc_function = pa->pa_function;
+ myx_ring_lock_init(&sc->sc_rx_ring_lock[MYX_RXSMALL]);
myx_bufs_init(&sc->sc_rx_buf_free[MYX_RXSMALL]);
myx_bufs_init(&sc->sc_rx_buf_list[MYX_RXSMALL]);
+ myx_ring_lock_init(&sc->sc_rx_ring_lock[MYX_RXBIG]);
myx_bufs_init(&sc->sc_rx_buf_free[MYX_RXBIG]);
myx_bufs_init(&sc->sc_rx_buf_list[MYX_RXBIG]);
+ myx_ring_lock_init(&sc->sc_tx_ring_lock);
myx_bufs_init(&sc->sc_tx_buf_free);
myx_bufs_init(&sc->sc_tx_buf_list);
timeout_set(&sc->sc_refill, myx_refill, sc);
+ mtx_init(&sc->sc_sts_mtx, IPL_NET);
+
+
/* Map the PCI memory space */
memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, MYXBAR0);
if (pci_mapreg_map(pa, MYXBAR0, memtype, BUS_SPACE_MAP_PREFETCHABLE,
@@ -457,8 +475,8 @@ myx_attachhook(void *arg)
goto freecmd;
}
- sc->sc_irqh = pci_intr_establish(sc->sc_pc, sc->sc_ih, IPL_NET,
- myx_intr, sc, DEVNAME(sc));
+ sc->sc_irqh = pci_intr_establish(sc->sc_pc, sc->sc_ih,
+ IPL_NET | IPL_MPSAFE, myx_intr, sc, DEVNAME(sc));
if (sc->sc_irqh == NULL) {
printf("%s: unable to establish interrupt\n", DEVNAME(sc));
goto freecmd;
@@ -821,6 +839,8 @@ void
myx_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
struct myx_softc *sc = (struct myx_softc *)ifp->if_softc;
+ bus_dmamap_t map = sc->sc_sts_dma.mxm_map;
+ u_int32_t sts;
imr->ifm_active = IFM_ETHER | IFM_AUTO;
if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
@@ -828,7 +848,15 @@ myx_media_status(struct ifnet *ifp, struct ifmediareq *imr)
return;
}
- myx_link_state(sc);
+ mtx_enter(&sc->sc_sts_mtx);
+ bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
+ BUS_DMASYNC_POSTREAD);
+ sts = sc->sc_sts->ms_linkstate;
+ bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
+ BUS_DMASYNC_PREREAD);
+ mtx_leave(&sc->sc_sts_mtx);
+
+ myx_link_state(sc, sts);
imr->ifm_status = IFM_AVALID;
if (!LINK_STATE_IS_UP(ifp->if_link_state))
@@ -840,12 +868,12 @@ myx_media_status(struct ifnet *ifp, struct ifmediareq *imr)
}
void
-myx_link_state(struct myx_softc *sc)
+myx_link_state(struct myx_softc *sc, u_int32_t sts)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
int link_state = LINK_STATE_DOWN;
- if (betoh32(sc->sc_sts->ms_linkstate) == MYXSTS_LINKUP)
+ if (betoh32(sts) == MYXSTS_LINKUP)
link_state = LINK_STATE_FULL_DUPLEX;
if (ifp->if_link_state != link_state) {
ifp->if_link_state = link_state;
@@ -1291,30 +1319,34 @@ myx_down(struct myx_softc *sc)
struct myx_cmd mc;
int s;
- s = splnet();
+ mtx_enter(&sc->sc_sts_mtx);
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_POSTREAD);
sc->sc_linkdown = sc->sc_sts->ms_linkdown;
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREREAD);
+ mtx_leave(&sc->sc_sts_mtx);
memset(&mc, 0, sizeof(mc));
(void)myx_cmd(sc, MYXCMD_SET_IFDOWN, &mc, NULL);
+ mtx_enter(&sc->sc_sts_mtx);
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_POSTREAD);
while (sc->sc_linkdown == sc->sc_sts->ms_linkdown) {
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREREAD);
- tsleep(sc->sc_sts, 0, "myxdown", 0);
+ msleep(sc->sc_sts, &sc->sc_sts_mtx, 0, "myxdown", 0);
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_POSTREAD);
}
+ mtx_leave(&sc->sc_sts_mtx);
timeout_del(&sc->sc_refill);
+ s = splnet();
CLR(ifp->if_flags, IFF_RUNNING);
if (ifp->if_link_state != LINK_STATE_UNKNOWN) {
@@ -1573,14 +1605,20 @@ myx_intr(void *arg)
struct ifnet *ifp = &sc->sc_ac.ac_if;
volatile struct myx_status *sts = sc->sc_sts;
bus_dmamap_t map = sc->sc_sts_dma.mxm_map;
- u_int32_t data;
+ u_int32_t data, link;
int refill = 0;
u_int8_t valid = 0;
+ u_int if_flags;
int i;
- if (!ISSET(ifp->if_flags, IFF_RUNNING))
+ KERNEL_LOCK();
+ if_flags = ifp->if_flags;
+ KERNEL_UNLOCK();
+
+ if (!ISSET(if_flags, IFF_RUNNING))
return (0);
+ mtx_enter(&sc->sc_sts_mtx);
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_POSTREAD);
@@ -1588,6 +1626,7 @@ myx_intr(void *arg)
if (valid == 0x0) {
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREREAD);
+ mtx_leave(&sc->sc_sts_mtx);
return (0);
}
sts->ms_isvalid = 0;
@@ -1598,31 +1637,39 @@ myx_intr(void *arg)
sc->sc_irqdeassertoff, &data, sizeof(data));
}
- if (!ISSET(ifp->if_flags, IFF_UP) &&
+ if (!ISSET(if_flags, IFF_UP) &&
sc->sc_linkdown != sts->ms_linkdown) {
/* myx_down is waiting for us */
wakeup_one(sc->sc_sts);
}
- if (sts->ms_statusupdated)
- myx_link_state(sc);
+ link = sts->ms_statusupdated ? sts->ms_linkstate : 0xffffffff;
do {
data = betoh32(sts->ms_txdonecnt);
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREREAD);
+ mtx_leave(&sc->sc_sts_mtx);
if (data != sc->sc_tx_count)
myx_txeof(sc, data);
refill |= myx_rxeof(sc);
+ mtx_enter(&sc->sc_sts_mtx);
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_POSTREAD);
} while (sts->ms_isvalid);
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREREAD);
+ mtx_leave(&sc->sc_sts_mtx);
+
+ if (link != 0xffffffff) {
+ KERNEL_LOCK();
+ myx_link_state(sc, link);
+ KERNEL_UNLOCK();
+ }
data = htobe32(3);
if (valid & 0x1) {
@@ -1634,15 +1681,17 @@ myx_intr(void *arg)
bus_space_barrier(sc->sc_memt, sc->sc_memh,
sc->sc_irqclaimoff, sizeof(data) * 2, BUS_SPACE_BARRIER_WRITE);
- if (ISSET(ifp->if_flags, IFF_OACTIVE)) {
+ if (ISSET(if_flags, IFF_OACTIVE)) {
+ KERNEL_LOCK();
CLR(ifp->if_flags, IFF_OACTIVE);
myx_start(ifp);
+ KERNEL_UNLOCK();
}
for (i = 0; i < 2; i++) {
if (ISSET(refill, 1 << i)) {
- myx_rx_fill(sc, i);
- if (myx_bufs_empty(&sc->sc_rx_buf_list[i]))
+ if (myx_rx_fill(sc, i) >= 0 &&
+ myx_bufs_empty(&sc->sc_rx_buf_list[i]))
timeout_add(&sc->sc_refill, 0);
}
}
@@ -1655,15 +1704,12 @@ myx_refill(void *xsc)
{
struct myx_softc *sc = xsc;
int i;
- int s;
- s = splnet();
for (i = 0; i < 2; i++) {
- myx_rx_fill(sc, i);
- if (myx_bufs_empty(&sc->sc_rx_buf_list[i]))
+ if (myx_rx_fill(sc, i) >= 0 &&
+ myx_bufs_empty(&sc->sc_rx_buf_list[i]))
timeout_add(&sc->sc_refill, 1);
}
- splx(s);
}
void
@@ -1690,12 +1736,14 @@ myx_txeof(struct myx_softc *sc, u_int32_t done_count)
bus_dmamap_sync(sc->sc_dmat, map, 0,
map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+
+ KERNEL_LOCK();
bus_dmamap_unload(sc->sc_dmat, map);
m_freem(m);
+ ifp->if_opackets++;
+ KERNEL_UNLOCK();
myx_buf_put(&sc->sc_tx_buf_free, mb);
-
- ifp->if_opackets++;
} while (++sc->sc_tx_count != done_count);
}
@@ -1730,24 +1778,26 @@ myx_rxeof(struct myx_softc *sc)
bus_dmamap_sync(sc->sc_dmat, mb->mb_map, 0,
mb->mb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
- bus_dmamap_unload(sc->sc_dmat, mb->mb_map);
m = mb->mb_m;
m->m_data += ETHER_ALIGN;
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = len;
+ KERNEL_LOCK();
+ bus_dmamap_unload(sc->sc_dmat, mb->mb_map);
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif
ether_input_mbuf(ifp, m);
+ ifp->if_ipackets++;
+ KERNEL_UNLOCK();
myx_buf_put(&sc->sc_rx_buf_free[ring], mb);
SET(rings, 1 << ring);
- ifp->if_ipackets++;
}
bus_dmamap_sync(sc->sc_dmat, sc->sc_intrq_dma.mxm_map, 0,
@@ -1779,42 +1829,49 @@ myx_rx_fill(struct myx_softc *sc, int ring)
struct myx_buf *mb, *firstmb;
u_int32_t offset = sc->sc_rx_ring_offset[ring];
u_int idx, firstidx;
+ int rv = 1;
- firstmb = myx_buf_fill(sc, ring);
- if (firstmb == NULL)
- return (1);
+ if (!myx_ring_enter(&sc->sc_rx_ring_lock[ring]))
+ return (-1);
- myx_buf_put(&sc->sc_rx_buf_list[ring], firstmb);
+ do {
+ firstmb = myx_buf_fill(sc, ring);
+ if (firstmb == NULL)
+ continue;
- firstidx = sc->sc_rx_ring_idx[ring];
- idx = firstidx + 1;
- idx %= sc->sc_rx_ring_count;
+ rv = 0;
+ myx_buf_put(&sc->sc_rx_buf_list[ring], firstmb);
- while ((mb = myx_buf_fill(sc, ring)) != NULL) {
- myx_buf_put(&sc->sc_rx_buf_list[ring], mb);
+ firstidx = sc->sc_rx_ring_idx[ring];
+ idx = firstidx + 1;
+ idx %= sc->sc_rx_ring_count;
- rxd.rx_addr = htobe64(mb->mb_map->dm_segs[0].ds_addr);
- myx_bus_space_write(sc->sc_memt, sc->sc_memh,
- offset + idx * sizeof(rxd), &rxd, sizeof(rxd));
+ while ((mb = myx_buf_fill(sc, ring)) != NULL) {
+ myx_buf_put(&sc->sc_rx_buf_list[ring], mb);
- idx++;
- idx %= sc->sc_rx_ring_count;
- }
+ rxd.rx_addr = htobe64(mb->mb_map->dm_segs[0].ds_addr);
+ myx_bus_space_write(sc->sc_memt, sc->sc_memh,
+ offset + idx * sizeof(rxd), &rxd, sizeof(rxd));
- /* make sure the first descriptor is seen after the others */
- if (idx != firstidx + 1) {
- bus_space_barrier(sc->sc_memt, sc->sc_memh,
- offset, sizeof(rxd) * sc->sc_rx_ring_count,
- BUS_SPACE_BARRIER_WRITE);
- }
+ idx++;
+ idx %= sc->sc_rx_ring_count;
+ }
+
+ /* make sure the first descriptor is seen after the others */
+ if (idx != firstidx + 1) {
+ bus_space_barrier(sc->sc_memt, sc->sc_memh,
+ offset, sizeof(rxd) * sc->sc_rx_ring_count,
+ BUS_SPACE_BARRIER_WRITE);
+ }
- rxd.rx_addr = htobe64(firstmb->mb_map->dm_segs[0].ds_addr);
- myx_write(sc, offset + firstidx * sizeof(rxd),
- &rxd, sizeof(rxd));
+ rxd.rx_addr = htobe64(firstmb->mb_map->dm_segs[0].ds_addr);
+ myx_write(sc, offset + firstidx * sizeof(rxd),
+ &rxd, sizeof(rxd));
- sc->sc_rx_ring_idx[ring] = idx;
+ sc->sc_rx_ring_idx[ring] = idx;
+ } while (!myx_ring_leave(&sc->sc_rx_ring_lock[ring]));
- return (0);
+ return (rv);
}
struct myx_buf *
@@ -1823,8 +1880,11 @@ myx_buf_fill(struct myx_softc *sc, int ring)
static size_t sizes[2] = { MCLBYTES, 12 * 1024 };
struct myx_buf *mb;
struct mbuf *m;
+ int rv;
+ KERNEL_LOCK();
m = MCLGETI(NULL, M_DONTWAIT, &sc->sc_ac.ac_if, sizes[ring]);
+ KERNEL_UNLOCK();
if (m == NULL)
return (NULL);
m->m_len = m->m_pkthdr.len = sizes[ring];
@@ -1833,8 +1893,10 @@ myx_buf_fill(struct myx_softc *sc, int ring)
if (mb == NULL)
goto mfree;
- if (bus_dmamap_load_mbuf(sc->sc_dmat, mb->mb_map, m,
- BUS_DMA_NOWAIT) != 0)
+ KERNEL_LOCK();
+ rv = bus_dmamap_load_mbuf(sc->sc_dmat, mb->mb_map, m, BUS_DMA_NOWAIT);
+ KERNEL_UNLOCK();
+ if (rv != 0)
goto put;
mb->mb_m = m;
@@ -1846,7 +1908,9 @@ myx_buf_fill(struct myx_softc *sc, int ring)
put:
myx_buf_put(&sc->sc_rx_buf_free[ring], mb);
mfree:
+ KERNEL_LOCK();
m_freem(m);
+ KERNEL_UNLOCK();
return (NULL);
}
@@ -1917,3 +1981,38 @@ myx_bufs_init(struct myx_buf_list *mbl)
SIMPLEQ_INIT(&mbl->mbl_q);
mtx_init(&mbl->mbl_mtx, IPL_NET);
}
+
+void
+myx_ring_lock_init(struct myx_ring_lock *mrl)
+{
+ mtx_init(&mrl->mrl_mtx, IPL_NET);
+ mrl->mrl_running = 0;
+}
+
+int
+myx_ring_enter(struct myx_ring_lock *mrl)
+{
+ int rv = 1;
+
+ mtx_enter(&mrl->mrl_mtx);
+ if (++mrl->mrl_running > 1)
+ rv = 0;
+ mtx_leave(&mrl->mrl_mtx);
+
+ return (rv);
+}
+
+int
+myx_ring_leave(struct myx_ring_lock *mrl)
+{
+ int rv = 1;
+
+ mtx_enter(&mrl->mrl_mtx);
+ if (--mrl->mrl_running > 0) {
+ mrl->mrl_running = 1;
+ rv = 0;
+ }
+ mtx_leave(&mrl->mrl_mtx);
+
+ return (rv);
+}