icnss: Wait uninterruptible for unregister driver

When WLAN driver calls unregister driver, an event is posted to
event work queue and the calling thread waits for the event to
complete. In some cases like during suspend, calling thread gets
interrupted and wait_for_completion returns. Once it is returned,
WLAN module resources are freed including code segments and work
queue gets into Synchronous Abort.
Fix the issue by making unregister uninterruptible to make sure
WLAN driver clean-up happens gracefully without interrupted by
user space.

CRs-fixed: 1073854
Change-Id: Id0f8634641fa2be12ffe00ddbc96a9e400e40739
Signed-off-by: Prashanth Bhatta <bhattap@codeaurora.org>
This commit is contained in:
Prashanth Bhatta 2016-10-05 17:37:42 -07:00
parent 5972a06743
commit 0da8e4a21c

View file

@ -261,6 +261,11 @@ void *icnss_ipc_log_long_context;
#define ICNSS_EVENT_PENDING 2989
#define ICNSS_EVENT_SYNC BIT(0)
#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
ICNSS_EVENT_SYNC)
enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
ICNSS_DRIVER_EVENT_SERVER_EXIT,
@ -565,16 +570,16 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
};
static int icnss_driver_event_post(enum icnss_driver_event_type type,
bool sync, void *data)
u32 flags, void *data)
{
struct icnss_driver_event *event;
unsigned long flags;
unsigned long irq_flags;
int gfp = GFP_KERNEL;
int ret = 0;
icnss_pr_dbg("Posting event: %s: %s%s(%d), state: 0x%lx\n",
current->comm, icnss_driver_event_to_str(type),
sync ? "-sync" : "", type, penv->state);
icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
icnss_driver_event_to_str(type), type, current->comm,
flags, penv->state);
if (type >= ICNSS_DRIVER_EVENT_MAX) {
icnss_pr_err("Invalid Event type: %d, can't post", type);
@ -594,31 +599,35 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
event->data = data;
init_completion(&event->complete);
event->ret = ICNSS_EVENT_PENDING;
event->sync = sync;
event->sync = !!(flags & ICNSS_EVENT_SYNC);
spin_lock_irqsave(&penv->event_lock, flags);
spin_lock_irqsave(&penv->event_lock, irq_flags);
list_add_tail(&event->list, &penv->event_list);
spin_unlock_irqrestore(&penv->event_lock, flags);
spin_unlock_irqrestore(&penv->event_lock, irq_flags);
penv->stats.events[type].posted++;
queue_work(penv->event_wq, &penv->event_work);
if (!sync)
if (!(flags & ICNSS_EVENT_SYNC))
goto out;
if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
wait_for_completion(&event->complete);
else
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);
spin_lock_irqsave(&penv->event_lock, irq_flags);
if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
event->sync = false;
spin_unlock_irqrestore(&penv->event_lock, flags);
spin_unlock_irqrestore(&penv->event_lock, irq_flags);
ret = -EINTR;
goto out;
}
spin_unlock_irqrestore(&penv->event_lock, flags);
spin_unlock_irqrestore(&penv->event_lock, irq_flags);
ret = event->ret;
kfree(event);
@ -2225,7 +2234,7 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
switch (msg_id) {
case QMI_WLFW_FW_READY_IND_V01:
icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
false, NULL);
0, NULL);
break;
case QMI_WLFW_MSA_READY_IND_V01:
icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
@ -2647,12 +2656,12 @@ static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
switch (code) {
case QMI_SERVER_ARRIVE:
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
false, NULL);
0, NULL);
break;
case QMI_SERVER_EXIT:
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
false, NULL);
0, NULL);
break;
default:
icnss_pr_dbg("Invalid code: %ld", code);
@ -2689,7 +2698,7 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
event_data->crashed = notif->crashed;
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
true, event_data);
ICNSS_EVENT_SYNC, event_data);
return NOTIFY_OK;
}
@ -2764,7 +2773,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
event_data->crashed = true;
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
true, event_data);
ICNSS_EVENT_SYNC, event_data);
break;
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state);
@ -2936,7 +2945,7 @@ int icnss_register_driver(struct icnss_driver_ops *ops)
}
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
true, ops);
ICNSS_EVENT_SYNC, ops);
if (ret == -ERESTARTSYS)
ret = 0;
@ -2964,7 +2973,7 @@ int icnss_unregister_driver(struct icnss_driver_ops *ops)
}
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
true, NULL);
ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
out:
return ret;
}
@ -3865,6 +3874,7 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
seq_puts(s, "MSA0 ASSIGNED");
continue;
case ICNSS_WLFW_EXISTS:
seq_puts(s, "WLAN FW EXISTS");
continue;
}