Commit be8b3943 authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo

ath10k: make WMI commands block by design

This will be necessary for further changes in
command submission scheme.

Once HTC is cleaned up WMI commands will finally
block.

This requires for SWBA to be processed in a
non-atomic context for now. Once other necessary
changes are in this will be reverted.
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 88e65fc3
...@@ -114,6 +114,7 @@ struct ath10k_wmi { ...@@ -114,6 +114,7 @@ struct ath10k_wmi {
struct completion unified_ready; struct completion unified_ready;
atomic_t pending_tx_count; atomic_t pending_tx_count;
wait_queue_head_t wq; wait_queue_head_t wq;
wait_queue_head_t tx_credits_wq;
struct sk_buff_head wmi_event_list; struct sk_buff_head wmi_event_list;
struct work_struct wmi_event_work; struct work_struct wmi_event_work;
......
...@@ -111,26 +111,29 @@ TRACE_EVENT(ath10k_log_dbg_dump, ...@@ -111,26 +111,29 @@ TRACE_EVENT(ath10k_log_dbg_dump,
); );
TRACE_EVENT(ath10k_wmi_cmd, TRACE_EVENT(ath10k_wmi_cmd,
TP_PROTO(int id, void *buf, size_t buf_len), TP_PROTO(int id, void *buf, size_t buf_len, int ret),
TP_ARGS(id, buf, buf_len), TP_ARGS(id, buf, buf_len, ret),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(unsigned int, id) __field(unsigned int, id)
__field(size_t, buf_len) __field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len) __dynamic_array(u8, buf, buf_len)
__field(int ret)
), ),
TP_fast_assign( TP_fast_assign(
__entry->id = id; __entry->id = id;
__entry->buf_len = buf_len; __entry->buf_len = buf_len;
__entry->ret = ret;
memcpy(__get_dynamic_array(buf), buf, buf_len); memcpy(__get_dynamic_array(buf), buf, buf_len);
), ),
TP_printk( TP_printk(
"id %d len %zu", "id %d len %zu ret %d",
__entry->id, __entry->id,
__entry->buf_len __entry->buf_len,
__entry->ret
) )
); );
......
...@@ -90,13 +90,12 @@ static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) ...@@ -90,13 +90,12 @@ static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
wake_up(&ar->wmi.wq); wake_up(&ar->wmi.wq);
} }
/* WMI command API */ static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, enum wmi_cmd_id cmd_id)
enum wmi_cmd_id cmd_id)
{ {
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct wmi_cmd_hdr *cmd_hdr; struct wmi_cmd_hdr *cmd_hdr;
int status; int ret;
u32 cmd = 0; u32 cmd = 0;
if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL) if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
...@@ -107,26 +106,40 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, ...@@ -107,26 +106,40 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
cmd_hdr = (struct wmi_cmd_hdr *)skb->data; cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
cmd_hdr->cmd_id = __cpu_to_le32(cmd); cmd_hdr->cmd_id = __cpu_to_le32(cmd);
if (atomic_add_return(1, &ar->wmi.pending_tx_count) >
WMI_MAX_PENDING_TX_COUNT) {
/* avoid using up memory when FW hangs */
dev_kfree_skb(skb);
atomic_dec(&ar->wmi.pending_tx_count);
return -EBUSY;
}
memset(skb_cb, 0, sizeof(*skb_cb)); memset(skb_cb, 0, sizeof(*skb_cb));
ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len, ret);
trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len); if (ret)
goto err_pull;
status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); return 0;
if (status) {
err_pull:
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
return ret;
}
static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar,
enum ath10k_htc_ep_id eid)
{
wake_up(&ar->wmi.tx_credits_wq);
}
static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
enum wmi_cmd_id cmd_id)
{
int ret = -EINVAL;
wait_event_timeout(ar->wmi.tx_credits_wq, ({
ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
(ret != -EAGAIN);
}), 3*HZ);
if (ret)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
atomic_dec(&ar->wmi.pending_tx_count);
return status;
}
return 0; return ret;
} }
static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
...@@ -1168,7 +1181,6 @@ static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) ...@@ -1168,7 +1181,6 @@ static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
/* some events require to be handled ASAP /* some events require to be handled ASAP
* thus can't be defered to a worker thread */ * thus can't be defered to a worker thread */
switch (event_id) { switch (event_id) {
case WMI_HOST_SWBA_EVENTID:
case WMI_MGMT_RX_EVENTID: case WMI_MGMT_RX_EVENTID:
ath10k_wmi_event_process(ar, skb); ath10k_wmi_event_process(ar, skb);
return; return;
...@@ -1186,6 +1198,7 @@ int ath10k_wmi_attach(struct ath10k *ar) ...@@ -1186,6 +1198,7 @@ int ath10k_wmi_attach(struct ath10k *ar)
init_completion(&ar->wmi.service_ready); init_completion(&ar->wmi.service_ready);
init_completion(&ar->wmi.unified_ready); init_completion(&ar->wmi.unified_ready);
init_waitqueue_head(&ar->wmi.wq); init_waitqueue_head(&ar->wmi.wq);
init_waitqueue_head(&ar->wmi.tx_credits_wq);
skb_queue_head_init(&ar->wmi.wmi_event_list); skb_queue_head_init(&ar->wmi.wmi_event_list);
INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work); INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work);
...@@ -1215,6 +1228,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar) ...@@ -1215,6 +1228,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
/* these fields are the same for all service endpoints */ /* these fields are the same for all service endpoints */
conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete; conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete;
conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx; conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx;
conn_req.ep_ops.ep_tx_credits = ath10k_wmi_op_ep_tx_credits;
/* connect to control service */ /* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
......
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