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:
parent
5972a06743
commit
0da8e4a21c
1 changed files with 31 additions and 21 deletions
|
@ -259,7 +259,12 @@ void *icnss_ipc_log_context;
|
|||
void *icnss_ipc_log_long_context;
|
||||
#endif
|
||||
|
||||
#define ICNSS_EVENT_PENDING 2989
|
||||
#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,
|
||||
|
@ -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;
|
||||
|
||||
ret = wait_for_completion_interruptible(&event->complete);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue