Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754848AbcJLQFz (ORCPT ); Wed, 12 Oct 2016 12:05:55 -0400 Received: from bh-25.webhostbox.net ([208.91.199.152]:48993 "EHLO bh-25.webhostbox.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755534AbcJLQDw (ORCPT ); Wed, 12 Oct 2016 12:03:52 -0400 Date: Wed, 12 Oct 2016 09:03:45 -0700 From: Guenter Roeck To: Douglas Anderson Cc: Thomas Gleixner , John Stultz , Andreas Mohr , briannorris@chromium.org, huangtao@rock-chips.com, tony.xie@rock-chips.com, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org Subject: Re: [v2] timers: Fix usleep_range() in the context of wake_up_process() Message-ID: <20161012160309.GA19146@roeck-us.net> References: <1476133442-17757-1-git-send-email-dianders@chromium.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1476133442-17757-1-git-send-email-dianders@chromium.org> User-Agent: Mutt/1.5.23 (2014-03-12) X-Authenticated_sender: guenter@roeck-us.net X-OutGoing-Spam-Status: No, score=-1.0 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - bh-25.webhostbox.net X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - roeck-us.net X-Get-Message-Sender-Via: bh-25.webhostbox.net: authenticated_id: guenter@roeck-us.net X-Authenticated-Sender: bh-25.webhostbox.net: guenter@roeck-us.net X-Source: X-Source-Args: X-Source-Dir: Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 2867 Lines: 81 On Mon, Oct 10, 2016 at 02:04:02PM -0700, Douglas Anderson wrote: > Users of usleep_range() expect that it will _never_ return in less time > than the minimum passed parameter. However, nothing in any of the code > ensures this. Specifically: > > usleep_range() => do_usleep_range() => schedule_hrtimeout_range() => > schedule_hrtimeout_range_clock() just ends up calling schedule() with an > appropriate timeout set using the hrtimer. If someone else happens to > wake up our task then we'll happily return from usleep_range() early. > > msleep() already has code to handle this case since it will loop as long > as there was still time left. usleep_range() had no such loop. > > The problem is is easily demonstrated with a small bit of test code: > > static int usleep_test_task(void *data) > { > atomic_t *done = data; > ktime_t start, end; > > start = ktime_get(); > usleep_range(50000, 100000); > end = ktime_get(); > pr_info("Requested 50000 - 100000 us. Actually slept for %llu us\n", > (unsigned long long)ktime_to_us(ktime_sub(end, start))); > atomic_set(done, 1); > > return 0; > } > > static void run_usleep_test(void) > { > struct task_struct *t; > atomic_t done; > > atomic_set(&done, 0); > > t = kthread_run(usleep_test_task, &done, "usleep_test_task"); > while (!atomic_read(&done)) { > wake_up_process(t); > udelay(1000); > } > kthread_stop(t); > } > > If you run the above code without this patch you get things like: > Requested 50000 - 100000 us. Actually slept for 967 us > > If you run the above code _with_ this patch, you get: > Requested 50000 - 100000 us. Actually slept for 50001 us > > Presumably this problem was not detected before because: > - It's not terribly common to use wake_up_process() directly. > - Other ways for processes to wake up are not typically mixed with > usleep_range(). > - There aren't lots of places that use usleep_range(), since many people > call either msleep() or udelay(). > > Reported-by: Tao Huang > Signed-off-by: Douglas Anderson > Reviewed-by: Brian Norris > Reviewed-by: Andreas Mohr Reviewed-by: Guenter Roeck The following drivers may expect the function to be interruptible. drivers/iio/accel/kxcjk-1013.c: kxcjk1013_runtime_resume() drivers/iio/accel/bmc150-accel-core.c:bmc150_accel_runtime_resume() drivers/iio/accel/mma8452.c:mma8452_runtime_resume() drivers/iio/accel/mma9551_core.c:mma9551_sleep() kernel/trace/ring_buffer.c:rb_test() A possible solution might be to introduce usleep_range_interruptible() and use it there. Note: drivers/scsi/mvumi.c:mvumi_rescan_bus() uses msleep() but should possibly use msleep_interruptible() instead. Guenter