diff mbox

RFC: mmc: block: replace semaphore with freezing

Message ID 1479293464-4576-1-git-send-email-linus.walleij@linaro.org
State New
Headers show

Commit Message

Linus Walleij Nov. 16, 2016, 10:51 a.m. UTC
The MMC/SD block core request processing thread is taking a
semaphore in the request processing section and the same
semaphore is taken around suspend/resume operations.

The purpose of the semaphore is to block any request from being
issued to the MMC/SD host controllers when the system is
suspended. A semaphore is used in place of a mutex since the
calls are coming from different threads.

This construction predates the kernel thread freezer mechanism:
we can easily replace the semaphore by instead activating the
freezer with set_freezable() and call try_to_freeze() instead
of the up(); schedule(); down(); construction that is devised
to let the suspend/resume calls get in and grab the semaphore.

Tested with a few suspend/resume to memory cycles on the Ux500
when doing intense dd operations in the background: the
thread thaws and resumes operation after resume.

Cc: linux-pm@vger.kernel.org
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
I haven't seen any need to preserve the call to schedule()
in the request processing thread, but I want advice on whether
that has a point. I would guess the thread will just anyway
be preempted if needed anyway as it is marked interruptible?
---
 drivers/mmc/card/queue.c | 13 ++-----------
 drivers/mmc/card/queue.h |  1 -
 2 files changed, 2 insertions(+), 12 deletions(-)

Comments

Arnd Bergmann Nov. 16, 2016, 12:19 p.m. UTC | #1
On Wednesday, November 16, 2016 11:51:04 AM CET Linus Walleij wrote:
> @@ -95,12 +95,9 @@ static int mmc_queue_thread(void *d)
>                                 set_current_state(TASK_RUNNING);
>                                 break;
>                         }
> -                       up(&mq->thread_sem);
> -                       schedule();
> -                       down(&mq->thread_sem);
> +                       try_to_freeze();
> 

The schedule() here is where we wait for new requests to come in
from mmc_request_fn(), you can't remove that or you end up spinning
continuously.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael J. Wysocki Nov. 16, 2016, 12:46 p.m. UTC | #2
On Wed, Nov 16, 2016 at 11:51 AM, Linus Walleij
<linus.walleij@linaro.org> wrote:
> The MMC/SD block core request processing thread is taking a
> semaphore in the request processing section and the same
> semaphore is taken around suspend/resume operations.
>
> The purpose of the semaphore is to block any request from being
> issued to the MMC/SD host controllers when the system is
> suspended. A semaphore is used in place of a mutex since the
> calls are coming from different threads.
>
> This construction predates the kernel thread freezer mechanism:
> we can easily replace the semaphore by instead activating the
> freezer with set_freezable() and call try_to_freeze() instead
> of the up(); schedule(); down(); construction that is devised
> to let the suspend/resume calls get in and grab the semaphore.
>
> Tested with a few suspend/resume to memory cycles on the Ux500
> when doing intense dd operations in the background: the
> thread thaws and resumes operation after resume.

Well, we had a session at the KS regarding usage of the freezer on
kernel threads and the conclusion was to get rid of that (as opposed
to freezing user space, which is necessary IMO).  So this change would
go in the opposite direction. :-)

Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jiri Kosina Nov. 16, 2016, 12:57 p.m. UTC | #3
On Wed, 16 Nov 2016, Rafael J. Wysocki wrote:

> > The MMC/SD block core request processing thread is taking a
> > semaphore in the request processing section and the same
> > semaphore is taken around suspend/resume operations.
> >
> > The purpose of the semaphore is to block any request from being
> > issued to the MMC/SD host controllers when the system is
> > suspended. A semaphore is used in place of a mutex since the
> > calls are coming from different threads.
> >
> > This construction predates the kernel thread freezer mechanism:
> > we can easily replace the semaphore by instead activating the
> > freezer with set_freezable() and call try_to_freeze() instead
> > of the up(); schedule(); down(); construction that is devised
> > to let the suspend/resume calls get in and grab the semaphore.
> >
> > Tested with a few suspend/resume to memory cycles on the Ux500
> > when doing intense dd operations in the background: the
> > thread thaws and resumes operation after resume.
> 
> Well, we had a session at the KS regarding usage of the freezer on
> kernel threads and the conclusion was to get rid of that (as opposed
> to freezing user space, which is necessary IMO).  So this change would
> go in the opposite direction. :-)

[ thanks for CCing me, Rafael ]

Agreed. You already have PM callbacks handled properly, so the way this 
should be done is once you're in the PM-callback due to system going 
through power management change, you just stop generation of any new I/O, 
and tell the kthread that it should schedule itself out.

Plus the schedule() has to stay there anyway, as try_to_freeze() is not 
going to provide you with the schedule() semantics unless the system is 
actually undergoing a PM transition towards suspend.

So either semaphore, or some kind of atomic flag, is exactly the 
information that should be passed from the PM callback to the kthread.

(now, I agree, that this is one of the very rare cases where the kthread 
freezer is actually being used properly -- IOW to pause a kthread that is 
actually generating new I/O ... but given the fact that this is so rare, 
and the API is so heavily abused, I really tend to heavily prefer a 
semaphore / flag based aproach).

Thanks,
Linus Walleij Nov. 16, 2016, 3:20 p.m. UTC | #4
On Wed, Nov 16, 2016 at 1:46 PM, Rafael J. Wysocki <rafael@kernel.org> wrote:

> Well, we had a session at the KS regarding usage of the freezer on
> kernel threads and the conclusion was to get rid of that (as opposed
> to freezing user space, which is necessary IMO).  So this change would
> go in the opposite direction. :-)

Aha so I should not make this thread look like everyone else, instead
everyone else should look like this thread, haha :D

Ah well, I'll just drop it.

Linus
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann Nov. 16, 2016, 4:32 p.m. UTC | #5
On Wednesday, November 16, 2016 4:20:47 PM CET Linus Walleij wrote:
> On Wed, Nov 16, 2016 at 1:46 PM, Rafael J. Wysocki <rafael@kernel.org> wrote:
> 
> > Well, we had a session at the KS regarding usage of the freezer on
> > kernel threads and the conclusion was to get rid of that (as opposed
> > to freezing user space, which is necessary IMO).  So this change would
> > go in the opposite direction. 
> 
> Aha so I should not make this thread look like everyone else, instead
> everyone else should look like this thread, haha 
> 
> Ah well, I'll just drop it.

It would still be good to remove the semaphore and do something else,
as we also want to remove all semaphores. ;-)

We could check "mq->flags & MMC_QUEUE_SUSPENDED" in the kthread to see
if the queue is currently suspended, and otherwise go to sleep there,
and then call wake_up() in the resume function.

While looking at that code, I just noticed that access to 
mq->flags is racy and should be fixed as well.

	Arnd

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij Nov. 22, 2016, 8:54 a.m. UTC | #6
On Wed, Nov 16, 2016 at 5:32 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Wednesday, November 16, 2016 4:20:47 PM CET Linus Walleij wrote:
>> On Wed, Nov 16, 2016 at 1:46 PM, Rafael J. Wysocki <rafael@kernel.org> wrote:
>>
>> > Well, we had a session at the KS regarding usage of the freezer on
>> > kernel threads and the conclusion was to get rid of that (as opposed
>> > to freezing user space, which is necessary IMO).  So this change would
>> > go in the opposite direction.
>>
>> Aha so I should not make this thread look like everyone else, instead
>> everyone else should look like this thread, haha
>>
>> Ah well, I'll just drop it.
>
> It would still be good to remove the semaphore and do something else,
> as we also want to remove all semaphores. ;-)
>
> We could check "mq->flags & MMC_QUEUE_SUSPENDED" in the kthread to see
> if the queue is currently suspended, and otherwise go to sleep there,
> and then call wake_up() in the resume function.

Hm... so simply:

if (mq->flags & MMC_QUEUE_SUSPENDED)
  schedule();

?

This whole kthread business is pretty messy. I would prefer if I could
just convert it to a workqueue. Just that it's not very simple the way
it speculates around in the request queue from the block layer.

> While looking at that code, I just noticed that access to
> mq->flags is racy and should be fixed as well.

I guess we should take the queue lock &q->lock around accessing
the flags.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Russell King - ARM Linux admin Nov. 22, 2016, 9:10 a.m. UTC | #7
On Tue, Nov 22, 2016 at 09:54:18AM +0100, Linus Walleij wrote:
> On Wed, Nov 16, 2016 at 5:32 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Wednesday, November 16, 2016 4:20:47 PM CET Linus Walleij wrote:
> >> On Wed, Nov 16, 2016 at 1:46 PM, Rafael J. Wysocki <rafael@kernel.org> wrote:
> >>
> >> > Well, we had a session at the KS regarding usage of the freezer on
> >> > kernel threads and the conclusion was to get rid of that (as opposed
> >> > to freezing user space, which is necessary IMO).  So this change would
> >> > go in the opposite direction.
> >>
> >> Aha so I should not make this thread look like everyone else, instead
> >> everyone else should look like this thread, haha
> >>
> >> Ah well, I'll just drop it.
> >
> > It would still be good to remove the semaphore and do something else,
> > as we also want to remove all semaphores. ;-)
> >
> > We could check "mq->flags & MMC_QUEUE_SUSPENDED" in the kthread to see
> > if the queue is currently suspended, and otherwise go to sleep there,
> > and then call wake_up() in the resume function.
> 
> Hm... so simply:
> 
> if (mq->flags & MMC_QUEUE_SUSPENDED)
>   schedule();
> 
> ?

No, the schedule() is required when there are no more requests to
process, so the thread doesn't spin waiting for the next request to
appear.
Arnd Bergmann Nov. 22, 2016, 9:16 a.m. UTC | #8
On Tuesday, November 22, 2016 9:54:18 AM CET Linus Walleij wrote:
> On Wed, Nov 16, 2016 at 5:32 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Wednesday, November 16, 2016 4:20:47 PM CET Linus Walleij wrote:
> >> On Wed, Nov 16, 2016 at 1:46 PM, Rafael J. Wysocki <rafael@kernel.org> wrote:
> >>
> >> > Well, we had a session at the KS regarding usage of the freezer on
> >> > kernel threads and the conclusion was to get rid of that (as opposed
> >> > to freezing user space, which is necessary IMO).  So this change would
> >> > go in the opposite direction.
> >>
> >> Aha so I should not make this thread look like everyone else, instead
> >> everyone else should look like this thread, haha
> >>
> >> Ah well, I'll just drop it.
> >
> > It would still be good to remove the semaphore and do something else,
> > as we also want to remove all semaphores. 
> >
> > We could check "mq->flags & MMC_QUEUE_SUSPENDED" in the kthread to see
> > if the queue is currently suspended, and otherwise go to sleep there,
> > and then call wake_up() in the resume function.
> 
> Hm... so simply:
> 
> if (mq->flags & MMC_QUEUE_SUSPENDED)
>   schedule();
> 
> ?

Something like that.

> This whole kthread business is pretty messy. I would prefer if I could
> just convert it to a workqueue. Just that it's not very simple the way
> it speculates around in the request queue from the block layer.

I don't see how that would work, but might be worth trying.
After doing that, a simple flush_workqueue() might be enough
to take care of the suspend operation.

> > While looking at that code, I just noticed that access to
> > mq->flags is racy and should be fixed as well.
> 
> I guess we should take the queue lock &q->lock around accessing
> the flags.

Yes, either that, or use set_bit/test_bit/test_and_set_bit for
atomic access. For instance, this one

        if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
                mq->flags &= ~MMC_QUEUE_NEW_REQUEST;

Could be

	if (test_and_clear(MMC_QUEUE_NEW_REQUEST_BIT, &mq->flags))
			...


	Arnd

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 8037f73a109a..09a932ffe46e 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -55,8 +55,8 @@  static int mmc_queue_thread(void *d)
 	struct request_queue *q = mq->queue;
 
 	current->flags |= PF_MEMALLOC;
+	set_freezable();
 
-	down(&mq->thread_sem);
 	do {
 		struct request *req = NULL;
 
@@ -95,12 +95,9 @@  static int mmc_queue_thread(void *d)
 				set_current_state(TASK_RUNNING);
 				break;
 			}
-			up(&mq->thread_sem);
-			schedule();
-			down(&mq->thread_sem);
+			try_to_freeze();
 		}
 	} while (1);
-	up(&mq->thread_sem);
 
 	return 0;
 }
@@ -289,8 +286,6 @@  int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
 			goto cleanup_queue;
 	}
 
-	sema_init(&mq->thread_sem, 1);
-
 	mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",
 		host->index, subname ? subname : "");
 
@@ -424,8 +419,6 @@  void mmc_queue_suspend(struct mmc_queue *mq)
 		spin_lock_irqsave(q->queue_lock, flags);
 		blk_stop_queue(q);
 		spin_unlock_irqrestore(q->queue_lock, flags);
-
-		down(&mq->thread_sem);
 	}
 }
 
@@ -441,8 +434,6 @@  void mmc_queue_resume(struct mmc_queue *mq)
 	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);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 3c15a75bae86..fe10f94795de 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -53,7 +53,6 @@  struct mmc_queue_req {
 struct mmc_queue {
 	struct mmc_card		*card;
 	struct task_struct	*thread;
-	struct semaphore	thread_sem;
 	unsigned int		flags;
 #define MMC_QUEUE_SUSPENDED	(1 << 0)
 #define MMC_QUEUE_NEW_REQUEST	(1 << 1)