Fix various powersave races + optimize tx/rx pointer update when powersave is off.

PR:		kern/197143
Submitted by:	Andriy Voskoboinyk <s3erios@gmail.com>
This commit is contained in:
Adrian Chadd 2015-05-03 23:39:02 +00:00
parent 9b7cc717ed
commit 5cacb17fa2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=282400
2 changed files with 59 additions and 16 deletions

View File

@ -152,11 +152,14 @@ static int wpi_alloc_fwmem(struct wpi_softc *);
static void wpi_free_fwmem(struct wpi_softc *);
static int wpi_alloc_rx_ring(struct wpi_softc *);
static void wpi_update_rx_ring(struct wpi_softc *);
static void wpi_update_rx_ring_ps(struct wpi_softc *);
static void wpi_reset_rx_ring(struct wpi_softc *);
static void wpi_free_rx_ring(struct wpi_softc *);
static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *,
int);
static void wpi_update_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
static void wpi_update_tx_ring_ps(struct wpi_softc *,
struct wpi_tx_ring *);
static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
static int wpi_read_eeprom(struct wpi_softc *,
@ -521,6 +524,9 @@ wpi_attach(device_t dev)
ic->ic_scan_mindwell = wpi_scan_mindwell;
ic->ic_setregdomain = wpi_setregdomain;
sc->sc_update_rx_ring = wpi_update_rx_ring;
sc->sc_update_tx_ring = wpi_update_tx_ring;
wpi_radiotap_attach(sc);
callout_init_mtx(&sc->calib_to, &sc->rxon_mtx, 0);
@ -1088,6 +1094,12 @@ fail: wpi_free_rx_ring(sc);
static void
wpi_update_rx_ring(struct wpi_softc *sc)
{
WPI_WRITE(sc, WPI_FH_RX_WPTR, sc->rxq.cur & ~7);
}
static void
wpi_update_rx_ring_ps(struct wpi_softc *sc)
{
struct wpi_rx_ring *ring = &sc->rxq;
@ -1096,14 +1108,15 @@ wpi_update_rx_ring(struct wpi_softc *sc)
return;
}
if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) {
WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) {
DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: wakeup request\n",
__func__);
WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
ring->update = 1;
} else
WPI_WRITE(sc, WPI_FH_RX_WPTR, ring->cur & ~7);
} else {
wpi_update_rx_ring(sc);
WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
}
}
static void
@ -1247,19 +1260,27 @@ fail: wpi_free_tx_ring(sc, ring);
static void
wpi_update_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring)
{
WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
}
static void
wpi_update_tx_ring_ps(struct wpi_softc *sc, struct wpi_tx_ring *ring)
{
if (ring->update != 0) {
/* Wait for INT_WAKEUP event. */
return;
}
if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) {
WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) {
DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s (%d): requesting wakeup\n",
__func__, ring->qid);
WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
ring->update = 1;
} else
WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
} else {
wpi_update_tx_ring(sc, ring);
WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
}
}
static void
@ -2067,6 +2088,18 @@ wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc)
}
wakeup(&ring->cmd[desc->idx]);
if (desc->type == WPI_CMD_SET_POWER_MODE) {
WPI_TXQ_LOCK(sc);
if (sc->sc_flags & WPI_PS_PATH) {
sc->sc_update_rx_ring = wpi_update_rx_ring_ps;
sc->sc_update_tx_ring = wpi_update_tx_ring_ps;
} else {
sc->sc_update_rx_ring = wpi_update_rx_ring;
sc->sc_update_tx_ring = wpi_update_tx_ring;
}
WPI_TXQ_UNLOCK(sc);
}
}
static void
@ -2262,7 +2295,7 @@ wpi_notif_intr(struct wpi_softc *sc)
if (sc->rxq.cur % 8 == 0) {
/* Tell the firmware what we have processed. */
wpi_update_rx_ring(sc);
sc->sc_update_rx_ring(sc);
}
}
}
@ -2293,9 +2326,8 @@ wpi_wakeup_intr(struct wpi_softc *sc)
wpi_update_tx_ring(sc, ring);
}
}
WPI_TXQ_UNLOCK(sc);
WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
WPI_TXQ_UNLOCK(sc);
}
/*
@ -2595,7 +2627,7 @@ wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
/* Kick TX ring. */
ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT;
wpi_update_tx_ring(sc, ring);
sc->sc_update_tx_ring(sc, ring);
if (ring->qid < WPI_CMD_QUEUE_NUM) {
/* Mark TX ring as full if we reach a certain threshold. */
@ -3147,7 +3179,7 @@ wpi_cmd(struct wpi_softc *sc, int code, const void *buf, size_t size,
/* Kick command ring. */
ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT;
wpi_update_tx_ring(sc, ring);
sc->sc_update_tx_ring(sc, ring);
DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
@ -3648,8 +3680,13 @@ wpi_set_pslevel(struct wpi_softc *sc, uint8_t dtim, int level, int async)
pmgt = &wpi_pmgt[1][level];
memset(&cmd, 0, sizeof cmd);
if (level != 0) /* not CAM */
WPI_TXQ_LOCK(sc);
if (level != 0) { /* not CAM */
cmd.flags |= htole16(WPI_PS_ALLOW_SLEEP);
sc->sc_flags |= WPI_PS_PATH;
} else
sc->sc_flags &= ~WPI_PS_PATH;
WPI_TXQ_UNLOCK(sc);
/* Retrieve PCIe Active State Power Management (ASPM). */
reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1);
if (!(reg & 0x1)) /* L0s Entry disabled. */

View File

@ -164,6 +164,9 @@ struct wpi_softc {
struct ifnet *sc_ifp;
int sc_debug;
int sc_flags;
#define WPI_PS_PATH (1 << 0)
struct mtx sc_mtx;
struct mtx tx_mtx;
@ -210,6 +213,9 @@ struct wpi_softc {
struct mtx nt_mtx;
void (*sc_node_free)(struct ieee80211_node *);
void (*sc_update_rx_ring)(struct wpi_softc *);
void (*sc_update_tx_ring)(struct wpi_softc *,
struct wpi_tx_ring *);
struct wpi_rx_radiotap_header sc_rxtap;
struct wpi_tx_radiotap_header sc_txtap;