Commit 5440ce25 authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo

ath10k: prevent CE from looping indefinitely

The double while() could end up running forever.
Inner while() would complete very fast. However
the completion processing could take enough time
for more completions to flow in. In that case the
outer while() would not terminate and run again,
and again. This could happen especially on a slow
host system.

This could lead to a system freeze during heavy
traffic. Note: this doesn't solve all known
starvation issues yet.
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent b8a1e00f
...@@ -742,11 +742,6 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) ...@@ -742,11 +742,6 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
u32 ctrl_addr = ce_state->ctrl_addr; u32 ctrl_addr = ce_state->ctrl_addr;
void *transfer_context;
u32 buf;
unsigned int nbytes;
unsigned int id;
unsigned int flags;
int ret; int ret;
ret = ath10k_pci_wake(ar); ret = ath10k_pci_wake(ar);
...@@ -759,38 +754,15 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) ...@@ -759,38 +754,15 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
ath10k_ce_engine_int_status_clear(ar, ctrl_addr, ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
HOST_IS_COPY_COMPLETE_MASK); HOST_IS_COPY_COMPLETE_MASK);
if (ce_state->recv_cb) { spin_unlock_bh(&ar_pci->ce_lock);
/*
* Pop completed recv buffers and call the registered
* recv callback for each
*/
while (ath10k_ce_completed_recv_next_nolock(ce_state,
&transfer_context,
&buf, &nbytes,
&id, &flags) == 0) {
spin_unlock_bh(&ar_pci->ce_lock);
ce_state->recv_cb(ce_state, transfer_context, buf,
nbytes, id, flags);
spin_lock_bh(&ar_pci->ce_lock);
}
}
if (ce_state->send_cb) { if (ce_state->recv_cb)
/* ce_state->recv_cb(ce_state);
* Pop completed send buffers and call the registered
* send callback for each if (ce_state->send_cb)
*/ ce_state->send_cb(ce_state);
while (ath10k_ce_completed_send_next_nolock(ce_state,
&transfer_context, spin_lock_bh(&ar_pci->ce_lock);
&buf,
&nbytes,
&id) == 0) {
spin_unlock_bh(&ar_pci->ce_lock);
ce_state->send_cb(ce_state, transfer_context,
buf, nbytes, id);
spin_lock_bh(&ar_pci->ce_lock);
}
}
/* /*
* Misc CE interrupts are not being handled, but still need * Misc CE interrupts are not being handled, but still need
...@@ -881,11 +853,7 @@ void ath10k_ce_disable_interrupts(struct ath10k *ar) ...@@ -881,11 +853,7 @@ void ath10k_ce_disable_interrupts(struct ath10k *ar)
} }
void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
void (*send_cb)(struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *),
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id),
int disable_interrupts) int disable_interrupts)
{ {
struct ath10k *ar = ce_state->ar; struct ath10k *ar = ce_state->ar;
...@@ -898,12 +866,7 @@ void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, ...@@ -898,12 +866,7 @@ void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
} }
void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
void (*recv_cb)(struct ath10k_ce_pipe *ce_state, void (*recv_cb)(struct ath10k_ce_pipe *))
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags))
{ {
struct ath10k *ar = ce_state->ar; struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
......
...@@ -116,17 +116,8 @@ struct ath10k_ce_pipe { ...@@ -116,17 +116,8 @@ struct ath10k_ce_pipe {
u32 ctrl_addr; u32 ctrl_addr;
void (*send_cb) (struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *);
void *per_transfer_send_context, void (*recv_cb)(struct ath10k_ce_pipe *);
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id);
void (*recv_cb) (struct ath10k_ce_pipe *ce_state,
void *per_transfer_recv_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags);
unsigned int src_sz_max; unsigned int src_sz_max;
struct ath10k_ce_ring *src_ring; struct ath10k_ce_ring *src_ring;
...@@ -181,11 +172,7 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, ...@@ -181,11 +172,7 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
unsigned int flags); unsigned int flags);
void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
void (*send_cb)(struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *),
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id),
int disable_interrupts); int disable_interrupts);
/* Append a simple buffer (address/length) to a sendlist. */ /* Append a simple buffer (address/length) to a sendlist. */
...@@ -228,12 +215,7 @@ int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, ...@@ -228,12 +215,7 @@ int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
u32 buffer); u32 buffer);
void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
void (*recv_cb)(struct ath10k_ce_pipe *ce_state, void (*recv_cb)(struct ath10k_ce_pipe *));
void *transfer_context,
u32 buffer,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags));
/* recv flags */ /* recv flags */
/* Data is byte-swapped */ /* Data is byte-swapped */
......
...@@ -612,19 +612,20 @@ struct ath10k_pci_compl *get_free_compl(struct ath10k_pci_pipe *pipe_info) ...@@ -612,19 +612,20 @@ struct ath10k_pci_compl *get_free_compl(struct ath10k_pci_pipe *pipe_info)
} }
/* Called by lower (CE) layer when a send to Target completes. */ /* Called by lower (CE) layer when a send to Target completes. */
static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state, static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state)
void *transfer_context,
u32 ce_data,
unsigned int nbytes,
unsigned int transfer_id)
{ {
struct ath10k *ar = ce_state->ar; struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id]; struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
struct ath10k_pci_compl *compl; struct ath10k_pci_compl *compl;
bool process = false; void *transfer_context;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
do { while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
&ce_data, &nbytes,
&transfer_id) == 0) {
/* /*
* For the send completion of an item in sendlist, just * For the send completion of an item in sendlist, just
* increment num_sends_allowed. The upper layer callback will * increment num_sends_allowed. The upper layer callback will
...@@ -655,38 +656,28 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state, ...@@ -655,38 +656,28 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state,
spin_lock_bh(&ar_pci->compl_lock); spin_lock_bh(&ar_pci->compl_lock);
list_add_tail(&compl->list, &ar_pci->compl_process); list_add_tail(&compl->list, &ar_pci->compl_process);
spin_unlock_bh(&ar_pci->compl_lock); spin_unlock_bh(&ar_pci->compl_lock);
}
process = true;
} while (ath10k_ce_completed_send_next(ce_state,
&transfer_context,
&ce_data, &nbytes,
&transfer_id) == 0);
/*
* If only some of the items within a sendlist have completed,
* don't invoke completion processing until the entire sendlist
* has been sent.
*/
if (!process)
return;
ath10k_pci_process_ce(ar); ath10k_pci_process_ce(ar);
} }
/* Called by lower (CE) layer when data is received from the Target. */ /* Called by lower (CE) layer when data is received from the Target. */
static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state, static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state)
void *transfer_context, u32 ce_data,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags)
{ {
struct ath10k *ar = ce_state->ar; struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id]; struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
struct ath10k_pci_compl *compl; struct ath10k_pci_compl *compl;
struct sk_buff *skb; struct sk_buff *skb;
void *transfer_context;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
unsigned int flags;
do { while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
&ce_data, &nbytes, &transfer_id,
&flags) == 0) {
compl = get_free_compl(pipe_info); compl = get_free_compl(pipe_info);
if (!compl) if (!compl)
break; break;
...@@ -709,12 +700,7 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state, ...@@ -709,12 +700,7 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state,
spin_lock_bh(&ar_pci->compl_lock); spin_lock_bh(&ar_pci->compl_lock);
list_add_tail(&compl->list, &ar_pci->compl_process); list_add_tail(&compl->list, &ar_pci->compl_process);
spin_unlock_bh(&ar_pci->compl_lock); spin_unlock_bh(&ar_pci->compl_lock);
}
} while (ath10k_ce_completed_recv_next(ce_state,
&transfer_context,
&ce_data, &nbytes,
&transfer_id,
&flags) == 0);
ath10k_pci_process_ce(ar); ath10k_pci_process_ce(ar);
} }
...@@ -1491,13 +1477,16 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, ...@@ -1491,13 +1477,16 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
return ret; return ret;
} }
static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state, static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
void *transfer_context,
u32 data,
unsigned int nbytes,
unsigned int transfer_id)
{ {
struct bmi_xfer *xfer = transfer_context; struct bmi_xfer *xfer;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data,
&nbytes, &transfer_id))
return;
if (xfer->wait_for_resp) if (xfer->wait_for_resp)
return; return;
...@@ -1505,14 +1494,17 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state, ...@@ -1505,14 +1494,17 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state,
complete(&xfer->done); complete(&xfer->done);
} }
static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state, static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
void *transfer_context,
u32 data,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags)
{ {
struct bmi_xfer *xfer = transfer_context; struct bmi_xfer *xfer;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
unsigned int flags;
if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data,
&nbytes, &transfer_id, &flags))
return;
if (!xfer->wait_for_resp) { if (!xfer->wait_for_resp) {
ath10k_warn("unexpected: BMI data received; ignoring\n"); ath10k_warn("unexpected: BMI data received; ignoring\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment