Commit 95904475 authored by Russell King's avatar Russell King

[MMC] Fix mmc_block suspend/resume handling (again).

The previous change is not the whole story - the mmc queue
thread may be handling a request.  We must wait for outstanding
requests to complete before allowing the suspend to proceed,
otherwise we may suffer loss of data.
parent e7f10a52
......@@ -428,11 +428,7 @@ static int mmc_blk_suspend(struct mmc_card *card, u32 state)
struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) {
unsigned long flags;
spin_lock_irqsave(&md->lock, flags);
blk_stop_queue(md->queue.queue);
spin_unlock_irqrestore(&md->lock, flags);
mmc_queue_suspend(&md->queue);
}
return 0;
}
......@@ -442,12 +438,8 @@ static int mmc_blk_resume(struct mmc_card *card)
struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) {
unsigned long flags;
mmc_blk_set_blksize(md, card);
spin_lock_irqsave(&md->lock, flags);
blk_start_queue(md->queue.queue);
spin_unlock_irqrestore(&md->lock, flags);
mmc_queue_resume(&md->queue);
}
return 0;
}
......
......@@ -15,6 +15,9 @@
#include <linux/mmc/host.h>
#include "mmc_queue.h"
#define MMC_QUEUE_EXIT (1 << 0)
#define MMC_QUEUE_SUSPENDED (1 << 1)
/*
* Prepare a MMC request. Essentially, this means passing the
* preparation off to the media driver. The media driver will
......@@ -66,14 +69,9 @@ static int mmc_queue_thread(void *d)
daemonize("mmcqd");
spin_lock_irq(&current->sighand->siglock);
sigfillset(&current->blocked);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
mq->thread = current;
complete(&mq->thread_complete);
down(&mq->thread_sem);
add_wait_queue(&mq->thread_wq, &wait);
do {
struct request *req = NULL;
......@@ -85,9 +83,11 @@ static int mmc_queue_thread(void *d)
spin_unlock(q->queue_lock);
if (!req) {
if (!mq->thread)
if (mq->flags & MMC_QUEUE_EXIT)
break;
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
continue;
}
set_current_state(TASK_RUNNING);
......@@ -95,6 +95,7 @@ static int mmc_queue_thread(void *d)
mq->issue_fn(mq, req);
} while (1);
remove_wait_queue(&mq->thread_wq, &wait);
up(&mq->thread_sem);
complete_and_exit(&mq->thread_complete, 0);
return 0;
......@@ -160,17 +161,61 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
return ret;
}
EXPORT_SYMBOL(mmc_init_queue);
void mmc_cleanup_queue(struct mmc_queue *mq)
{
mq->thread = NULL;
mq->flags |= MMC_QUEUE_EXIT;
wake_up(&mq->thread_wq);
wait_for_completion(&mq->thread_complete);
blk_cleanup_queue(mq->queue);
mq->card = NULL;
}
EXPORT_SYMBOL(mmc_cleanup_queue);
/**
* mmc_queue_suspend - suspend a MMC request queue
* @mq: MMC queue to suspend
*
* Stop the block request queue, and wait for our thread to
* complete any outstanding requests. This ensures that we
* won't suspend while a request is being processed.
*/
void mmc_queue_suspend(struct mmc_queue *mq)
{
request_queue_t *q = mq->queue;
unsigned long flags;
if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
mq->flags |= MMC_QUEUE_SUSPENDED;
spin_lock_irqsave(q->queue_lock, flags);
blk_stop_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
down(&mq->thread_sem);
}
}
EXPORT_SYMBOL(mmc_queue_suspend);
/**
* mmc_queue_resume - resume a previously suspended MMC request queue
* @mq: MMC queue to resume
*/
void mmc_queue_resume(struct mmc_queue *mq)
{
request_queue_t *q = mq->queue;
unsigned long flags;
if (mq->flags & MMC_QUEUE_SUSPENDED) {
mq->flags &= ~MMC_QUEUE_SUSPENDED;
up(&mq->thread_sem);
spin_lock_irqsave(q->queue_lock, flags);
blk_start_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
}
}
EXPORT_SYMBOL(mmc_queue_resume);
......@@ -8,7 +8,8 @@ struct mmc_queue {
struct mmc_card *card;
struct completion thread_complete;
wait_queue_head_t thread_wq;
struct task_struct *thread;
struct semaphore thread_sem;
unsigned int flags;
struct request *req;
int (*prep_fn)(struct mmc_queue *, struct request *);
int (*issue_fn)(struct mmc_queue *, struct request *);
......@@ -25,5 +26,7 @@ struct mmc_io_request {
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
extern void mmc_cleanup_queue(struct mmc_queue *);
extern void mmc_queue_suspend(struct mmc_queue *);
extern void mmc_queue_resume(struct mmc_queue *);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment