diff options
author | Johan Hovold <johan@hovoldconsulting.com> | 2015-09-14 20:19:04 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2015-09-14 21:57:25 -0700 |
commit | b9154df5cfd2d4cc180f425f0b26e095ce1ffdbc (patch) | |
tree | 6fc4c03faa2710fe0ab2c300aad7f326573cdda7 /drivers/staging/greybus/sdio.c | |
parent | e5265266ec6360702feb6ff69d1aec9e318ae1f6 (diff) |
greybus: sdio: fix work-queue leak and use-after-free
A single global work-queue pointer was used for the per-connection
workqueue, something which would lead to memory leaks and all sorts of
bad things if there are ever more than one SDIO connection in a system.
Also add the missing error handling when allocating the queue.
Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/sdio.c')
-rw-r--r-- | drivers/staging/greybus/sdio.c | 19 |
1 files changed, 12 insertions, 7 deletions
diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 14617e31782e..bfa1181074d6 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -25,6 +25,7 @@ struct gb_sdio_host { void *xfer_buffer; spinlock_t xfer; /* lock to cancel ongoing transfer */ bool xfer_stop; + struct workqueue_struct *mrq_workqueue; struct work_struct mrqwork; u8 queued_events; bool removed; @@ -32,7 +33,6 @@ struct gb_sdio_host { bool read_only; }; -static struct workqueue_struct *gb_sdio_mrq_workqueue; #define GB_SDIO_RSP_R1_R5_R6_R7 (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ GB_SDIO_RSP_OPCODE) @@ -497,7 +497,7 @@ static void gb_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) goto out; } - queue_work(gb_sdio_mrq_workqueue, &host->mrqwork); + queue_work(host->mrq_workqueue, &host->mrqwork); mutex_unlock(&host->lock); return; @@ -710,7 +710,12 @@ static int gb_sdio_connection_init(struct gb_connection *connection) } mutex_init(&host->lock); spin_lock_init(&host->xfer); - gb_sdio_mrq_workqueue = alloc_workqueue("gb_sdio_mrq", 0, 1); + host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, + dev_name(&connection->dev)); + if (!host->mrq_workqueue) { + ret = -ENOMEM; + goto free_buffer; + } INIT_WORK(&host->mrqwork, gb_sdio_mrq_work); ret = mmc_add_host(mmc); @@ -723,9 +728,9 @@ static int gb_sdio_connection_init(struct gb_connection *connection) return ret; free_work: - destroy_workqueue(gb_sdio_mrq_workqueue); + destroy_workqueue(host->mrq_workqueue); +free_buffer: kfree(host->xfer_buffer); - free_mmc: connection->private = NULL; mmc_free_host(mmc); @@ -747,8 +752,8 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) connection->private = NULL; mutex_unlock(&host->lock); - flush_workqueue(gb_sdio_mrq_workqueue); - destroy_workqueue(gb_sdio_mrq_workqueue); + flush_workqueue(host->mrq_workqueue); + destroy_workqueue(host->mrq_workqueue); mmc_remove_host(mmc); kfree(host->xfer_buffer); mmc_free_host(mmc); |