wlan: Blocking request for firmware memory dump
Currently,fw memory dump requests from cfg80211 followed
an event based protocol.This has been modified to a
blocking mechanism with a timeout.Now only one fw mem dump
request can be serviced at a time.Request through ioctl is
also changed to the same.Status is sent to userspace in case of
requests through cfg80211 layer.
Change-Id: I359c71f995192ccb6dc766176160f3e1c1244787
CRs-Fixed: 930754
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 8f39865..66a6e71 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1227,12 +1227,34 @@
#define PROCFS_MEMDUMP_DIR "debug"
#define PROCFS_MEMDUMP_NAME "fwdump"
#define FW_MEM_DUMP_REQ_ID 1
+#define FW_MEM_DUMP_TIMEOUT_MS 3000
+#define FW_MEM_DUMP_MAGIC 0x3C3A2D44
+
+/**
+ * struct hdd_fw_mem_dump_req_ctx - hdd fw mem dump req context
+ *
+ * @magic : magic for validating cfg80211 requests
+ * @status: status for cfg80211 requests
+ * @pHDDCtx: ptr to HDD context
+ * @req_completion: completion variable for fw mem dump
+ */
+struct hdd_fw_mem_dump_req_ctx {
+ uint32_t magic;
+ bool status;
+ struct completion req_completion;
+};
+
+/**
+ * callback type to check fw mem dump request.Called from SVC
+ * context and update status in HDD.
+ */
+typedef void (*hdd_fw_mem_dump_req_cb)(struct hdd_fw_mem_dump_req_ctx *);
int memdump_init(void);
int memdump_deinit(void);
void wlan_hdd_fw_mem_dump_cb(void *,tAniFwrDumpRsp *);
int wlan_hdd_fw_mem_dump_req(hdd_context_t * pHddCtx);
-
+void wlan_hdd_fw_mem_dump_req_cb(struct hdd_fw_mem_dump_req_ctx*);
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
/**
* struct hdd_ll_stats_context - hdd link layer stats context
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 84c7f48..4d4dc5a 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -5852,6 +5852,16 @@
/*call common API for FW mem dump req*/
ret = wlan_hdd_fw_mem_dump_req(pHddCtx);
+ if (true == ret)
+ {
+ /*indicate to userspace the status of fw mem dump */
+ wlan_indicate_mem_dump_complete(true);
+ }
+ else
+ {
+ /*else send failure to userspace */
+ wlan_indicate_mem_dump_complete(false);
+ }
EXIT();
return ret;
}
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index d9f1df3..d150651 100755
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -12165,6 +12165,7 @@
int status;
hdd_context_t *hdd_ctx = (hdd_context_t *)PDE_DATA(file_inode(file));
size_t ret_count;
+ loff_t bytes_left;
ENTER();
hddLog(LOG1, FL("Read req for size:%zu pos:%llu"), count, *pos);
@@ -12180,12 +12181,13 @@
/* run fs_read_handler in an atomic context*/
vos_ssr_protect(__func__);
- ret_count = wlan_fwr_mem_dump_fsread_handler( buf, count, pos);
- if(ret_count == 0)
+ ret_count = wlan_fwr_mem_dump_fsread_handler( buf, count, pos, &bytes_left);
+ if(bytes_left == 0)
{
/*Free the fwr mem dump buffer */
wlan_free_fwr_mem_dump_buffer();
wlan_set_fwr_mem_dump_state(FW_MEM_DUMP_IDLE);
+ ret_count=0;
}
/*if SSR/unload code is waiting for memdump_read to finish,signal it*/
vos_ssr_unprotect(__func__);
@@ -12215,25 +12217,29 @@
void wlan_hdd_fw_mem_dump_cb(void *fwMemDumpReqContext,
tAniFwrDumpRsp *dump_rsp)
{
- hdd_context_t *pHddCtx = (hdd_context_t *)fwMemDumpReqContext;
- int status;
- ENTER();
- status = wlan_hdd_validate_context(pHddCtx);
- if (0 != status) {
- return;
- }
+ struct hdd_fw_mem_dump_req_ctx *pHddFwMemDumpCtx = (struct hdd_fw_mem_dump_req_ctx *)fwMemDumpReqContext;
+ ENTER();
+ spin_lock(&hdd_context_lock);
+ if(!pHddFwMemDumpCtx || (FW_MEM_DUMP_MAGIC != pHddFwMemDumpCtx->magic)) {
+ spin_unlock(&hdd_context_lock);
+ return;
+ }
+ /* report the status to requesting function and free mem.*/
if (dump_rsp->dump_status != eHAL_STATUS_SUCCESS) {
- hddLog(LOGE, FL("fw dump request declined by fwr"));
- //report failure to user space
- wlan_indicate_mem_dump_complete(false);
+ hddLog(LOGE, FL("fw dump request declined by fwr"));
+ //set the request completion variable
+ complete(&(pHddFwMemDumpCtx->req_completion));
//Free the allocated fwr dump
wlan_free_fwr_mem_dump_buffer();
wlan_set_fwr_mem_dump_state(FW_MEM_DUMP_IDLE);
- return;
}
- else
- hddLog(LOG1, FL("fw dump request accepted by fwr"));
+ else {
+ hddLog(LOG1, FL("fw dump request accepted by fwr"));
+ /* register the HDD callback which will be called by SVC */
+ wlan_set_svc_fw_mem_dump_req_cb((void*)wlan_hdd_fw_mem_dump_req_cb,(void*)pHddFwMemDumpCtx);
+ }
+ spin_unlock(&hdd_context_lock);
EXIT();
}
@@ -12370,9 +12376,11 @@
int wlan_hdd_fw_mem_dump_req(hdd_context_t * pHddCtx)
{
tAniFwrDumpReq fw_mem_dump_req={0};
+ struct hdd_fw_mem_dump_req_ctx fw_mem_dump_ctx;
eHalStatus status = eHAL_STATUS_FAILURE;
int ret=0;
ENTER();
+
/*Check whether a dump request is already going on
*Caution this function will free previously held memory if new dump request is allowed*/
if (!wlan_fwr_mem_dump_test_and_set_write_allowed_bit()) {
@@ -12390,18 +12398,61 @@
hddLog(LOGE, FL("Fwr mem Allocation failed"));
return -ENOMEM;
}
+ init_completion(&fw_mem_dump_ctx.req_completion);
+ fw_mem_dump_ctx.magic = FW_MEM_DUMP_MAGIC;
+ fw_mem_dump_ctx.status = false;
+
fw_mem_dump_req.fwMemDumpReqCallback = wlan_hdd_fw_mem_dump_cb;
- fw_mem_dump_req.fwMemDumpReqContext = pHddCtx;
+ fw_mem_dump_req.fwMemDumpReqContext = &fw_mem_dump_ctx;
status = sme_FwMemDumpReq(pHddCtx->hHal, &fw_mem_dump_req);
if(eHAL_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: fw_mem_dump_req failed ", __func__);
wlan_free_fwr_mem_dump_buffer();
+ ret = -EFAULT;
+ goto cleanup;
}
- EXIT();
+ /*wait for fw mem dump completion to send event to userspace*/
+ ret = wait_for_completion_timeout(&fw_mem_dump_ctx.req_completion,msecs_to_jiffies(FW_MEM_DUMP_TIMEOUT_MS));
+ if (0 >= ret )
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR,
+ "%s: fw_mem_dump_req timeout %d ", __func__,ret);
+ }
+cleanup:
+ spin_lock(&hdd_context_lock);
+ fw_mem_dump_ctx.magic = 0;
+ spin_unlock(&hdd_context_lock);
- return status;
+ EXIT();
+ return fw_mem_dump_ctx.status;
+}
+
+/**
+ * HDD callback which will be called by SVC to indicate mem dump completion.
+ */
+void wlan_hdd_fw_mem_dump_req_cb(struct hdd_fw_mem_dump_req_ctx* pHddFwMemDumpCtx)
+{
+ if (!pHddFwMemDumpCtx) {
+ hddLog(VOS_TRACE_LEVEL_ERROR,
+ "%s: HDD context not valid ", __func__);
+ return;
+ }
+ spin_lock(&hdd_context_lock);
+ /* check the req magic and set status */
+ if (pHddFwMemDumpCtx->magic == FW_MEM_DUMP_MAGIC)
+ {
+ pHddFwMemDumpCtx->status = true;
+ //signal the completion
+ complete(&(pHddFwMemDumpCtx->req_completion));
+ }
+ else
+ {
+ hddLog(VOS_TRACE_LEVEL_ERROR,
+ "%s: fw mem dump request possible timeout ", __func__);
+ }
+ spin_unlock(&hdd_context_lock);
}
void hdd_initialize_adapter_common(hdd_adapter_t *pAdapter)
diff --git a/CORE/SVC/inc/wlan_logging_sock_svc.h b/CORE/SVC/inc/wlan_logging_sock_svc.h
index d5afa33..5663cb1 100644
--- a/CORE/SVC/inc/wlan_logging_sock_svc.h
+++ b/CORE/SVC/inc/wlan_logging_sock_svc.h
@@ -66,6 +66,8 @@
void wlan_logging_set_log_level(void);
+#define FW_MEM_DUMP_MAGIC 0x3C3A2D44
+
enum FW_MEM_DUMP_STATE{
FW_MEM_DUMP_IDLE,
FW_MEM_DUMP_READ_IN_PROGRESS,
@@ -76,7 +78,8 @@
bool wlan_fwr_mem_dump_test_and_set_write_allowed_bit(void);
bool wlan_fwr_mem_dump_test_and_set_read_allowed_bit(void);
void wlan_set_fwr_mem_dump_state(enum FW_MEM_DUMP_STATE fw_mem_dump_state);
-size_t wlan_fwr_mem_dump_fsread_handler(char __user *buf, size_t count, loff_t *pos);
+void wlan_set_svc_fw_mem_dump_req_cb(void*,void*);
+size_t wlan_fwr_mem_dump_fsread_handler(char __user *buf, size_t count, loff_t *pos,loff_t* bytes_left);
void wlan_indicate_mem_dump_complete(bool );
void wlan_store_fwr_mem_dump_size(uint32 dump_size);
void wlan_free_fwr_mem_dump_buffer(void);
diff --git a/CORE/SVC/src/logging/wlan_logging_sock_svc.c b/CORE/SVC/src/logging/wlan_logging_sock_svc.c
index c5c7b03..7192b6d 100644
--- a/CORE/SVC/src/logging/wlan_logging_sock_svc.c
+++ b/CORE/SVC/src/logging/wlan_logging_sock_svc.c
@@ -124,10 +124,12 @@
unsigned int fw_mem_dump_pkt_drop_cnt;
/* Lock to synchronize of queue/dequeue of pkts in fw log pkt queue */
spinlock_t fw_mem_dump_lock;
- /* Fw memory dump state */
+ /* Fw memory dump status */
enum FW_MEM_DUMP_STATE fw_mem_dump_status;
- /* Completion variable for handling SSR/unload during copy_to_user */
- struct completion fw_mem_copy_to_user_completion;
+ /* storage for HDD callback which completes fw mem dump request */
+ void * svc_fw_mem_dump_req_cb;
+ /* storage for HDD callback which completes fw mem dump request arg */
+ void * svc_fw_mem_dump_req_cb_arg;
};
struct pkt_stats_msg {
@@ -870,7 +872,6 @@
VOS_STATUS status = VOS_STATUS_E_FAILURE;
unsigned long flags;
int byte_left = 0;
-
do {
spin_lock_irqsave(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock, flags);
@@ -918,20 +919,18 @@
(int)(gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc - gwlan_logging.fw_mem_dump_ctx.fw_dump_start_loc));
if(skb->len > byte_left)
{
- vos_mem_copy(gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc, &skb->data, byte_left);
+ vos_mem_copy(gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc, skb->data, byte_left);
//Update the current location ptr
gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc += byte_left;
}
else
{
- vos_mem_copy(gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc, &skb->data, skb->len);
+ vos_mem_copy(gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc, skb->data, skb->len);
//Update the current location ptr
gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc += skb->len;
}
}
spin_unlock_irqrestore(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock, flags);
- //if(skb)
- // pr_err("Mem dump buffer overflow byte_left %d skb->len %d", byte_left, skb->len);
/*return vos pkt since skb is already detached */
vos_pkt_return_packet(current_pkt);
} while (next_pkt);
@@ -1229,7 +1228,6 @@
int ret_wait_status = 0;
int ret = 0;
unsigned long flags;
-
set_user_nice(current, -2);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
@@ -1331,9 +1329,31 @@
fill_fw_mem_dump_buffer();
}
if(test_and_clear_bit(LOGGER_FW_MEM_DUMP_PKT_POST_DONE_MASK,&gwlan_logging.event_flag)){
- /*indicate to user space that mem dump is complete */
+ spin_lock_irqsave(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock,flags);
+ /*Chnage fw memory dump to indicate write done*/
+ gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_status = FW_MEM_DUMP_WRITE_DONE;
+ /*reset dropped packet count upon completion of this request*/
+ gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_pkt_drop_cnt = 0;
+ spin_unlock_irqrestore(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock,flags);
fill_fw_mem_dump_buffer();
- wlan_indicate_mem_dump_complete(true);
+ /*
+ * Call the registered HDD callback for indicating
+ * memdump complete. If it's null,then something is
+ * not right.
+ */
+ if (gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb &&
+ gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb_arg) {
+ ((hdd_fw_mem_dump_req_cb)
+ gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb)(
+ (struct hdd_fw_mem_dump_req_ctx*)
+ gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb_arg);
+
+ /*invalidate the callback pointers*/
+ spin_lock_irqsave(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock,flags);
+ gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb = NULL;
+ gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb_arg = NULL;
+ spin_unlock_irqrestore(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock,flags);
+ }
}
if (test_and_clear_bit(HOST_PKT_STATS_POST_MASK,
@@ -2032,6 +2052,8 @@
pr_err("%s: fw_mem_dump_req alloc failed for size %d bytes", __func__,gwlan_logging.fw_mem_dump_ctx.fw_dump_max_size);
return -ENOMEM;
}
+ vos_mem_zero(gwlan_logging.fw_mem_dump_ctx.fw_dump_start_loc,gwlan_logging.fw_mem_dump_ctx.fw_dump_max_size);
+
return 0;
}
@@ -2077,12 +2099,12 @@
gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_status = FW_MEM_DUMP_READ_IN_PROGRESS;
}
spin_unlock_irqrestore(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock, flags);
- pr_info("%s:fw mem dump state --> %d ", __func__,gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_status);
+ //pr_info("%s:fw mem dump state --> %d ", __func__,gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_status);
return ret;
}
size_t wlan_fwr_mem_dump_fsread_handler(char __user *buf,
- size_t count, loff_t *pos)
+ size_t count, loff_t *pos,loff_t* bytes_left)
{
if (buf == NULL || gwlan_logging.fw_mem_dump_ctx.fw_dump_start_loc == NULL)
{
@@ -2100,16 +2122,24 @@
count = gwlan_logging.fw_mem_dump_ctx.fw_dump_max_size - *pos;
}
if (copy_to_user(buf, gwlan_logging.fw_mem_dump_ctx.fw_dump_start_loc, count)) {
- pr_err("copy to user space failed");
+ pr_err("%s copy to user space failed",__func__);
return 0;
}
-
/* offset(pos) should be updated here based on the copy done*/
*pos += count;
-
+ *bytes_left = gwlan_logging.fw_mem_dump_ctx.fw_dump_max_size - *pos;
return count;
}
+void wlan_set_svc_fw_mem_dump_req_cb (void * fw_mem_dump_req_cb, void * fw_mem_dump_req_cb_arg)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock, flags);
+ gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb = fw_mem_dump_req_cb;
+ gwlan_logging.fw_mem_dump_ctx.svc_fw_mem_dump_req_cb_arg = fw_mem_dump_req_cb_arg;
+ spin_unlock_irqrestore(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock, flags);
+}
+
void wlan_free_fwr_mem_dump_buffer (void )
{
unsigned long flags;
@@ -2147,8 +2177,6 @@
void *vos_ctx;
int ret;
struct sk_buff *skb = NULL;
- unsigned long flags;
-
vos_ctx = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if (!vos_ctx) {
pr_err("Invalid VOS context");
@@ -2167,20 +2195,9 @@
return;
}
- spin_lock_irqsave(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock, flags);
- /*Chnage fw memory dump to indicate write done*/
- gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_status = FW_MEM_DUMP_WRITE_DONE;
- /*reset dropped packet count upon completion of this request*/
- gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_pkt_drop_cnt = 0;
- spin_unlock_irqrestore(&gwlan_logging.fw_mem_dump_ctx.fw_mem_dump_lock, flags);
- skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
- NULL,
-#endif
- sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN,
- QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP_INDEX,
- GFP_KERNEL);
+ skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
+ sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN);
if (!skb) {
pr_err("cfg80211_vendor_event_alloc failed");
@@ -2203,8 +2220,8 @@
goto nla_put_failure;
}
}
-
- cfg80211_vendor_event(skb, GFP_KERNEL);
+ /*indicate mem dump complete*/
+ cfg80211_vendor_cmd_reply(skb);
pr_info("Memdump event sent successfully to user space : recvd size %d",(int)(gwlan_logging.fw_mem_dump_ctx.fw_dump_current_loc - gwlan_logging.fw_mem_dump_ctx.fw_dump_start_loc));
return;