summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrashanth Bhatta <bhattap@codeaurora.org>2016-08-20 20:02:45 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-08-27 12:09:16 -0700
commit7d45313e05f7de3f210c09e40da5b438e397d880 (patch)
tree24821536c7d4bb4326550a18439915d7e5ebc4eb
parente8a0e0808d1862467d32aaee8982b5b6500ba89c (diff)
icnss: Handle wait being interrupted
When event needs to be processed synchronously, event posting thread waits for the completion. After completion, result from the event work is retrieved and event buffer would be freed. But if waiting thread gets interrupted then wait_for_completion API returns failure and it also frees the buffer posted for processing. Event work queue may accesses the freed buffer and crash the system. Fix the issue by properly synchronizing event buffer free between caller and event work by checking for return value of wait_for_completion. CRs-fixed: 1057180 Change-Id: Ic3968fd4c0232da6bc9a97d94376f540f62bd2e6 Signed-off-by: Prashanth Bhatta <bhattap@codeaurora.org>
-rw-r--r--drivers/soc/qcom/icnss.c38
1 files changed, 31 insertions, 7 deletions
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index aff9683b394f..1993b8e43c3e 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -217,6 +217,8 @@ module_param(quirks, ulong, 0600);
void *icnss_ipc_log_context;
+#define ICNSS_EVENT_PENDING 2989
+
enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
ICNSS_DRIVER_EVENT_SERVER_EXIT,
@@ -486,6 +488,7 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
event->type = type;
event->data = data;
init_completion(&event->complete);
+ event->ret = ICNSS_EVENT_PENDING;
event->sync = sync;
spin_lock_irqsave(&penv->event_lock, flags);
@@ -494,12 +497,26 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
penv->stats.events[type].posted++;
queue_work(penv->event_wq, &penv->event_work);
- if (sync) {
- ret = wait_for_completion_interruptible(&event->complete);
- if (ret == 0)
- ret = event->ret;
- kfree(event);
+
+ if (!sync)
+ return ret;
+
+ ret = wait_for_completion_interruptible(&event->complete);
+
+ icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
+ icnss_driver_event_to_str(type), type, penv->state, ret,
+ event->ret);
+
+ spin_lock_irqsave(&penv->event_lock, flags);
+ if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
+ event->sync = false;
+ spin_unlock_irqrestore(&penv->event_lock, flags);
+ return ret;
}
+ spin_unlock_irqrestore(&penv->event_lock, flags);
+
+ ret = event->ret;
+ kfree(event);
return ret;
}
@@ -2071,11 +2088,15 @@ static void icnss_driver_event_work(struct work_struct *work)
penv->stats.events[event->type].processed++;
+ spin_lock_irqsave(&penv->event_lock, flags);
if (event->sync) {
event->ret = ret;
complete(&event->complete);
- } else
- kfree(event);
+ continue;
+ }
+ spin_unlock_irqrestore(&penv->event_lock, flags);
+
+ kfree(event);
spin_lock_irqsave(&penv->event_lock, flags);
}
@@ -2372,6 +2393,9 @@ int icnss_register_driver(struct icnss_driver_ops *ops)
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
true, ops);
+ if (ret == -ERESTARTSYS)
+ ret = 0;
+
out:
return ret;
}