From c6cc8e73eb188156494e061bdb93966b0c7d9443 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 9 May 2016 10:59:01 +0530 Subject: [PATCH] greybus: fw-download: Replace timer with delayed-work The timeout-handlers need to call routines that can sleep and those can't be called from interrupt context. The timer-handler is called in interrupt context and so will hit a BUG() in vmalloc.c. This patch moves away from timers to delayed-work, whose timeout handler gets called in process context and can call the sleep-able routines safely. Note that this issue wasn't hit earlier when the initial patch for timeouts was implemented due to some issues in the build arche_420. But with the new build arche_440, the BUG started crashing the phone on timeouts and so this fix is required. Tested on EVT 1.5 by triggering fake timeouts, by not sending release-firmware request for example. This is tested with build arche_440. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/fw-download.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 0ebea37e6778..42cbbf4ac077 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "firmware.h" #include "greybus.h" @@ -29,7 +29,7 @@ struct fw_request { const struct firmware *fw; struct list_head node; - struct timer_list timer; + struct delayed_work dwork; /* Timeout, in jiffies, within which the firmware shall download */ unsigned long release_timeout_j; struct kref kref; @@ -129,9 +129,10 @@ static void free_firmware(struct fw_download *fw_download, put_fw_req(fw_req); } -static void fw_request_timedout(unsigned long data) +static void fw_request_timedout(struct work_struct *work) { - struct fw_request *fw_req = (struct fw_request *)data; + struct delayed_work *dwork = to_delayed_work(work); + struct fw_request *fw_req = container_of(dwork, struct fw_request, dwork); struct fw_download *fw_download = fw_req->fw_download; dev_err(fw_download->parent, @@ -207,11 +208,8 @@ static struct fw_request *find_firmware(struct fw_download *fw_download, req_count = DIV_ROUND_UP(fw_req->fw->size, MIN_FETCH_SIZE); fw_req->release_timeout_j = jiffies + req_count * NEXT_REQ_TIMEOUT_J; - init_timer(&fw_req->timer); - fw_req->timer.function = fw_request_timedout; - fw_req->timer.expires = jiffies + NEXT_REQ_TIMEOUT_J; - fw_req->timer.data = (unsigned long)fw_req; - add_timer(&fw_req->timer); + INIT_DELAYED_WORK(&fw_req->dwork, fw_request_timedout); + schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J); return fw_req; @@ -300,8 +298,8 @@ static int fw_download_fetch_firmware(struct gb_operation *op) return -EINVAL; } - /* Make sure timer handler isn't running in parallel */ - del_timer_sync(&fw_req->timer); + /* Make sure work handler isn't running in parallel */ + cancel_delayed_work_sync(&fw_req->dwork); /* We timed-out before reaching here ? */ if (fw_req->disabled) { @@ -344,7 +342,7 @@ static int fw_download_fetch_firmware(struct gb_operation *op) size); /* Refresh timeout */ - mod_timer(&fw_req->timer, jiffies + NEXT_REQ_TIMEOUT_J); + schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J); put_fw: put_fw_req(fw_req); @@ -377,7 +375,7 @@ static int fw_download_release_firmware(struct gb_operation *op) return -EINVAL; } - del_timer_sync(&fw_req->timer); + cancel_delayed_work_sync(&fw_req->dwork); free_firmware(fw_download, fw_req); put_fw_req(fw_req); @@ -459,7 +457,7 @@ void gb_fw_download_connection_exit(struct gb_connection *connection) /* Release pending firmware packages */ list_for_each_entry_safe(fw_req, tmp, &fw_download->fw_requests, node) { - del_timer_sync(&fw_req->timer); + cancel_delayed_work_sync(&fw_req->dwork); free_firmware(fw_download, fw_req); put_fw_req(fw_req); } -- 2.39.5