Skip to content

Commit

Permalink
brcmfmac: convert SDIO dpc implementation to workqueue
Browse files Browse the repository at this point in the history
Switch SDIO dpc implementation from kernel thread to workqueue for
better performance and compatibility.

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Franky Lin <frankyl@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Franky Lin authored and John W. Linville committed Sep 24, 2012
1 parent 1d38227 commit f1e68c2
Showing 1 changed file with 83 additions and 102 deletions.
185 changes: 83 additions & 102 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,6 @@ struct brcmf_sdio {

u32 hostintmask; /* Copy of Host Interrupt Mask */
u32 intstatus; /* Intstatus bits (events) pending */
bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
bool fcstate; /* State of dongle flow-control */

uint blocksize; /* Block size of SDIO transfers */
Expand Down Expand Up @@ -569,8 +568,8 @@ struct brcmf_sdio {
bool wd_timer_valid;
uint save_ms;

struct task_struct *dpc_tsk;
struct completion dpc_wait;
struct workqueue_struct *brcmf_wq;
struct work_struct datawork;
struct list_head dpc_tsklst;
spinlock_t dpc_tl_lock;

Expand Down Expand Up @@ -2183,12 +2182,6 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
bus->watchdog_tsk = NULL;
}

if (bus->dpc_tsk && bus->dpc_tsk != current) {
send_sig(SIGTERM, bus->dpc_tsk, 1);
kthread_stop(bus->dpc_tsk);
bus->dpc_tsk = NULL;
}

down(&bus->sdsem);

/* Enable clock for device interrupts */
Expand Down Expand Up @@ -2261,15 +2254,31 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)
}
#endif /* CONFIG_BRCMFMAC_SDIO_OOB */

static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
{
struct list_head *new_hd;
unsigned long flags;

if (in_interrupt())
new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
else
new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (new_hd == NULL)
return;

spin_lock_irqsave(&bus->dpc_tl_lock, flags);
list_add_tail(new_hd, &bus->dpc_tsklst);
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
}

static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
{
u32 intstatus, newstatus = 0;
uint rxlimit = bus->rxbound; /* Rx frames to read before resched */
uint txlimit = bus->txbound; /* Tx frames to send before resched */
uint framecnt = 0; /* Temporary counter of tx/rx frames */
bool rxdone = true; /* Flag for no more read data */
bool resched = false; /* Flag indicating resched wanted */
int err;
int err = 0;

brcmf_dbg(TRACE, "Enter\n");

Expand Down Expand Up @@ -2454,7 +2463,6 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
if (ret == 0)
bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;

brcmf_dbg(INFO, "Return_dpc value is : %d\n", ret);
bus->ctrl_frame_stat = false;
brcmf_sdbrcm_wait_event_wakeup(bus);
}
Expand All @@ -2477,15 +2485,13 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
bus->intstatus = 0;
} else if (bus->clkstate == CLK_PENDING) {
brcmf_dbg(INFO, "rescheduled due to CLK_PENDING awaiting I_CHIPACTIVE interrupt\n");
resched = true;
brcmf_sdbrcm_adddpctsk(bus);
} else if (bus->intstatus || atomic_read(&bus->ipend) > 0 ||
(!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
&& data_ok(bus)) || PKT_AVAILABLE()) {
resched = true;
brcmf_sdbrcm_adddpctsk(bus);
}

bus->dpc_sched = resched;

/* If we're done for now, turn off clock request. */
if ((bus->clkstate != CLK_PENDING)
&& bus->idletime == BRCMF_IDLE_IMMEDIATE) {
Expand All @@ -2494,65 +2500,6 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
}

up(&bus->sdsem);

return resched;
}

static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
{
struct list_head *new_hd;
unsigned long flags;

if (in_interrupt())
new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
else
new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (new_hd == NULL)
return;

spin_lock_irqsave(&bus->dpc_tl_lock, flags);
list_add_tail(new_hd, &bus->dpc_tsklst);
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
}

static int brcmf_sdbrcm_dpc_thread(void *data)
{
struct brcmf_sdio *bus = (struct brcmf_sdio *) data;
struct list_head *cur_hd, *tmp_hd;
unsigned long flags;

allow_signal(SIGTERM);
/* Run until signal received */
while (1) {
if (kthread_should_stop())
break;

if (list_empty(&bus->dpc_tsklst))
if (wait_for_completion_interruptible(&bus->dpc_wait))
break;

spin_lock_irqsave(&bus->dpc_tl_lock, flags);
list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);

if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
/* after stopping the bus, exit thread */
brcmf_sdbrcm_bus_stop(bus->sdiodev->dev);
bus->dpc_tsk = NULL;
spin_lock_irqsave(&bus->dpc_tl_lock, flags);
break;
}

if (brcmf_sdbrcm_dpc(bus))
brcmf_sdbrcm_adddpctsk(bus);

spin_lock_irqsave(&bus->dpc_tl_lock, flags);
list_del(cur_hd);
kfree(cur_hd);
}
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
}
return 0;
}

static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
Expand All @@ -2562,6 +2509,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
unsigned long flags;

brcmf_dbg(TRACE, "Enter\n");

Expand Down Expand Up @@ -2600,13 +2548,15 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
if (pktq_plen(&bus->txq, prec) > qcount[prec])
qcount[prec] = pktq_plen(&bus->txq, prec);
#endif
/* Schedule DPC if needed to send queued packet(s) */
if (!bus->dpc_sched) {
bus->dpc_sched = true;
if (bus->dpc_tsk) {
brcmf_sdbrcm_adddpctsk(bus);
complete(&bus->dpc_wait);
}

spin_lock_irqsave(&bus->dpc_tl_lock, flags);
if (list_empty(&bus->dpc_tsklst)) {
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);

brcmf_sdbrcm_adddpctsk(bus);
queue_work(bus->brcmf_wq, &bus->datawork);
} else {
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
}

return ret;
Expand Down Expand Up @@ -2802,6 +2752,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
unsigned long flags;

brcmf_dbg(TRACE, "Enter\n");

Expand Down Expand Up @@ -2885,9 +2836,15 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
} while (ret < 0 && retries++ < TXRETRIES);
}

if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) {
spin_lock_irqsave(&bus->dpc_tl_lock, flags);
if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) &&
list_empty(&bus->dpc_tsklst)) {
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);

bus->activity = false;
brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
} else {
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
}

up(&bus->sdsem);
Expand Down Expand Up @@ -3698,18 +3655,16 @@ void brcmf_sdbrcm_isr(void *arg)
if (!bus->intr)
brcmf_dbg(ERROR, "isr w/o interrupt configured!\n");

bus->dpc_sched = true;
if (bus->dpc_tsk) {
brcmf_sdbrcm_adddpctsk(bus);
complete(&bus->dpc_wait);
}
brcmf_sdbrcm_adddpctsk(bus);
queue_work(bus->brcmf_wq, &bus->datawork);
}

static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
{
#ifdef DEBUG
struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
#endif /* DEBUG */
unsigned long flags;

brcmf_dbg(TIMER, "Enter\n");

Expand All @@ -3726,14 +3681,20 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
if (!bus->intr ||
(bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {

if (!bus->dpc_sched) {
spin_lock_irqsave(&bus->dpc_tl_lock, flags);
if (list_empty(&bus->dpc_tsklst)) {
u8 devpend;
spin_unlock_irqrestore(&bus->dpc_tl_lock,
flags);
devpend = brcmf_sdio_regrb(bus->sdiodev,
SDIO_CCCR_INTx,
NULL);
intstatus =
devpend & (INTR_STATUS_FUNC1 |
INTR_STATUS_FUNC2);
} else {
spin_unlock_irqrestore(&bus->dpc_tl_lock,
flags);
}

/* If there is something, make like the ISR and
Expand All @@ -3742,11 +3703,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
bus->sdcnt.pollcnt++;
atomic_set(&bus->ipend, 1);

bus->dpc_sched = true;
if (bus->dpc_tsk) {
brcmf_sdbrcm_adddpctsk(bus);
complete(&bus->dpc_wait);
}
brcmf_sdbrcm_adddpctsk(bus);
queue_work(bus->brcmf_wq, &bus->datawork);
}
}

Expand Down Expand Up @@ -3800,6 +3758,26 @@ static bool brcmf_sdbrcm_chipmatch(u16 chipid)
return false;
}

static void brcmf_sdio_dataworker(struct work_struct *work)
{
struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
datawork);
struct list_head *cur_hd, *tmp_hd;
unsigned long flags;

spin_lock_irqsave(&bus->dpc_tl_lock, flags);
list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);

brcmf_sdbrcm_dpc(bus);

spin_lock_irqsave(&bus->dpc_tl_lock, flags);
list_del(cur_hd);
kfree(cur_hd);
}
spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
}

static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
{
brcmf_dbg(TRACE, "Enter\n");
Expand Down Expand Up @@ -4012,6 +3990,9 @@ static void brcmf_sdbrcm_release(struct brcmf_sdio *bus)
/* De-register interrupt handler */
brcmf_sdio_intr_unregister(bus->sdiodev);

cancel_work_sync(&bus->datawork);
destroy_workqueue(bus->brcmf_wq);

if (bus->sdiodev->bus_if->drvr) {
brcmf_detach(bus->sdiodev->dev);
brcmf_sdbrcm_release_dongle(bus);
Expand Down Expand Up @@ -4064,6 +4045,13 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
init_waitqueue_head(&bus->ctrl_wait);
init_waitqueue_head(&bus->dcmd_resp_wait);

bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");
if (bus->brcmf_wq == NULL) {
brcmf_dbg(ERROR, "insufficient memory to create txworkqueue\n");
goto fail;
}
INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);

/* Set up the watchdog timer */
init_timer(&bus->timer);
bus->timer.data = (unsigned long)bus;
Expand All @@ -4081,15 +4069,8 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
bus->watchdog_tsk = NULL;
}
/* Initialize DPC thread */
init_completion(&bus->dpc_wait);
INIT_LIST_HEAD(&bus->dpc_tsklst);
spin_lock_init(&bus->dpc_tl_lock);
bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread,
bus, "brcmf_dpc");
if (IS_ERR(bus->dpc_tsk)) {
pr_warn("brcmf_dpc thread failed to start\n");
bus->dpc_tsk = NULL;
}

/* Assign bus interface call back */
bus->sdiodev->bus_if->brcmf_bus_stop = brcmf_sdbrcm_bus_stop;
Expand Down

0 comments on commit f1e68c2

Please sign in to comment.