From: Daniel Lezcano <daniel.lezcano@linaro.org>
To: Viresh Kumar <viresh.kumar@linaro.org>
Cc: rjw@rjwysocki.net, linux-kernel@vger.kernel.org,
Eduardo Valentin <edubezval@gmail.com>,
Javi Merino <javi.merino@kernel.org>,
Leo Yan <leo.yan@linaro.org>,
Kevin Wangtao <kevin.wangtao@linaro.org>,
Vincent Guittot <vincent.guittot@linaro.org>,
Rui Zhang <rui.zhang@intel.com>,
Daniel Thompson <daniel.thompson@linaro.org>,
"open list:POWER MANAGEMENT CORE" <linux-pm@vger.kernel.org>
Subject: Re: [PATCH V5] powercap/drivers/idle_injection: Add an idle injection framework
Date: Tue, 5 Jun 2018 16:54:14 +0200 [thread overview]
Message-ID: <57d769f8-46ea-512e-8f89-a0439c9d053f@linaro.org> (raw)
In-Reply-To: <20180605103917.pyhhcobdvaivqv6g@vireshk-i7>
On 05/06/2018 12:39, Viresh Kumar wrote:
> On 05-06-18, 11:16, Daniel Lezcano wrote:
>> diff --git a/drivers/powercap/idle_injection.c b/drivers/powercap/idle_injection.c
>> +/**
>> + * idle_injection_wakeup - Wake up all idle injection threads
>> + * @ii_dev: the idle injection device
>> + *
>> + * Every idle injection task belonging to the idle injection device
>> + * and running on an online CPU will be wake up by this call.
>> + */
>> +static void idle_injection_wakeup(struct idle_injection_device *ii_dev)
>> +{
>> + struct idle_injection_thread *iit;
>> + int cpu;
>> +
>> + for_each_cpu_and(cpu, ii_dev->cpumask, cpu_online_mask) {
>> + atomic_inc(&ii_dev->count);
>
> This may not be sufficient enough in some extremely corner cases, as it is
> possible that the idle_injection_fn() starts running for the first cpu in the
> cpumask right after first iteration of this loop completes. And so
> atomic_dec_and_test() may return true there before idle_injection_fn() task gets
> a chance to run on all cpus in the cpumask. Normally this wouldn't be a big
> problem as the hrtimer can get reprogrammed in that case, but there is a case I
> am worried about. More on this later..
>
>> + iit = per_cpu_ptr(&idle_injection_thread, cpu);
>> + iit->should_run = 1;
>> + wake_up_process(iit->tsk);
>> + }
>> +}
>> +
>> +/**
>> + * idle_injection_wakeup_fn - idle injection timer callback
>> + * @timer: a hrtimer structure
>> + *
>> + * This function is called when the idle injection timer expires which
>> + * will wake up the idle injection tasks and these ones, in turn, play
>> + * idle a specified amount of time.
>> + *
>> + * Return: HRTIMER_NORESTART.
>> + */
>> +static enum hrtimer_restart idle_injection_wakeup_fn(struct hrtimer *timer)
>> +{
>> + struct idle_injection_device *ii_dev =
>> + container_of(timer, struct idle_injection_device, timer);
>> +
>> + idle_injection_wakeup(ii_dev);
>> +
>> + return HRTIMER_NORESTART;
>> +}
>> +
>> +/**
>> + * idle_injection_fn - idle injection routine
>> + * @cpu: the CPU number the tasks belongs to
>> + *
>> + * The idle injection routine will stay idle the specified amount of
>> + * time
>> + */
>> +static void idle_injection_fn(unsigned int cpu)
>> +{
>> + struct idle_injection_device *ii_dev;
>> + struct idle_injection_thread *iit;
>> + int run_duration_ms, idle_duration_ms;
>> +
>> + ii_dev = per_cpu(idle_injection_device, cpu);
>> + iit = per_cpu_ptr(&idle_injection_thread, cpu);
>> +
>> + /*
>> + * Boolean used by the smpboot main loop and used as a
>> + * flip-flop in this function
>> + */
>> + iit->should_run = 0;
>> +
>> + idle_duration_ms = atomic_read(&ii_dev->idle_duration_ms);
>> + if (idle_duration_ms)
>> + play_idle(idle_duration_ms);
>> +
>> + /*
>> + * The last CPU waking up is in charge of setting the timer. If
>> + * the CPU is hotplugged, the timer will move to another CPU
>> + * (which may not belong to the same cluster) but that is not a
>> + * problem as the timer will be set again by another CPU
>> + * belonging to the cluster. This mechanism is self adaptive.
>> + */
>> + if (!atomic_dec_and_test(&ii_dev->count))
>> + return;
>> +
>> + run_duration_ms = atomic_read(&ii_dev->run_duration_ms);
>> + if (run_duration_ms) {
>> + hrtimer_start(&ii_dev->timer, ms_to_ktime(run_duration_ms),
>> + HRTIMER_MODE_REL_PINNED);
>> + return;
>> + }
>> +
>> + complete(&ii_dev->stop_complete);
>> +}
>
> Here is the corner case I was talking about. Consider the cpumask contains
> CPU0/1/2/3.
>
> PATH A PATH B
>
> unregister()
> -> idle_injection_stop()
>
> idle_injection_wakeup()
> -> Running loop for CPU0
> -> atomic inc (count == 1)
> -> wake_up_process(tsk)
> -> At this point the current task stops
> running and idle_injection_fn() starts running
> (maybe on a different CPU)
>
>
>
>
> idle_injection_fn()
> -> atomic_dec_and_test(), returns true
> as count == 0
>
>
> -> set-duration to 0
> -> wait for completion
>
>
> -> atomic_read(run_duration), returns 0
> -> complete()
>
> -> kfree(ii_dev);
>
>
> But the initial loop idle_injection_wakeup() has only run for CPU0 until now and
> will continue running for other CPUs and will crash as ii_dev is already freed.
>
> Am I still making a mistake here ?
I don't think you are doing a mistake. Even if this can happen
theoretically, I don't think practically that is the case.
The play_idle() has 1ms minimum sleep time.
The scenario you are describing means:
1. the loop in idle_injection_wakeup() takes more than 1ms to achieve
2. at the same time, the user of the idle injection unregisters while
the idle injection is acting precisely at CPU0 and exits before another
task was wakeup by the loop in 1. more than 1ms after.
>From my POV, this scenario can't happen.
Anyway, we must write rock solid code so may be we can use a refcount to
protect against that, so instead of freeing in unregister, we refput the
ii_dev pointer.
>> +static struct idle_injection_device *ii_dev_alloc(void)
>> +{
>> + struct idle_injection_device *ii_dev;
>> +
>> + ii_dev = kzalloc(sizeof(*ii_dev), GFP_KERNEL);
>> + if (!ii_dev)
>> + return NULL;
>> +
>> + if (!alloc_cpumask_var(&ii_dev->cpumask, GFP_KERNEL)) {
>> + kfree(ii_dev);
>> + return NULL;
>> + }
>> +
>> + return ii_dev;
>> +}
>> +
>> +static void ii_dev_free(struct idle_injection_device *ii_dev)
>> +{
>> + free_cpumask_var(ii_dev->cpumask);
>> + kfree(ii_dev);
>> +}
>> +
>> +/**
>> + * idle_injection_register - idle injection init routine
>> + * @cpumask: the list of CPUs managed by the idle injection device
>> + *
>> + * This is the initialization function in charge of creating the
>> + * initializing of the timer and allocate the structures. It does not
>> + * starts the idle injection cycles.
>> + *
>> + * Return: NULL if an allocation fails.
>> + */
>> +struct idle_injection_device *idle_injection_register(struct cpumask *cpumask)
>> +{
>> + struct idle_injection_device *ii_dev;
>> + int cpu;
>> +
>> + ii_dev = ii_dev_alloc();
>> + if (!ii_dev)
>> + return NULL;
>> +
>> + cpumask_copy(ii_dev->cpumask, cpumask);
>> + hrtimer_init(&ii_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
>> + ii_dev->timer.function = idle_injection_wakeup_fn;
>> + init_completion(&ii_dev->stop_complete);
>> +
>> + for_each_cpu(cpu, ii_dev->cpumask) {
>> +
>> + if (per_cpu(idle_injection_device, cpu)) {
>> + pr_err("cpu%d is already registered\n", cpu);
>> + goto out_rollback_per_cpu;
>> + }
>> +
>> + per_cpu(idle_injection_device, cpu) = ii_dev;
>> + }
>> +
>> + return ii_dev;
>> +
>> +out_rollback_per_cpu:
>> + for_each_cpu(cpu, ii_dev->cpumask)
>> + per_cpu(idle_injection_device, cpu) = NULL;
>
> Maybe move above into ii_dev_free(), as I suggested earlier, as that will remove
> same code from unregister routine as well.
I don't like to mix operations when the function name define clearly
what is doing. The ii_dev_free() frees a structure,
idle_injection_device refers to a global variable.
Anyway if we are talking about using a refcount, we can add a release
which frees the structure and sets the per cpu idle_injection_device
global variable to NULL.
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
next prev parent reply other threads:[~2018-06-05 14:54 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-06-05 9:16 Daniel Lezcano
2018-06-05 10:39 ` Viresh Kumar
2018-06-05 14:54 ` Daniel Lezcano [this message]
2018-06-06 4:27 ` Viresh Kumar
2018-06-06 10:22 ` Daniel Lezcano
2018-06-06 10:45 ` Viresh Kumar
2018-06-06 12:05 ` Andrea Parri
2018-06-06 12:29 ` Peter Zijlstra
2018-06-07 14:11 ` Daniel Lezcano
2018-06-08 4:48 ` Viresh Kumar
2018-06-08 8:31 ` Daniel Lezcano
2018-06-06 12:23 ` Peter Zijlstra
2018-06-06 13:42 ` Daniel Lezcano
2018-06-06 15:02 ` Peter Zijlstra
2018-06-07 8:18 ` Daniel Lezcano
2018-06-07 8:32 ` Peter Zijlstra
2018-06-07 8:42 ` Viresh Kumar
2018-06-07 8:46 ` Daniel Lezcano
2018-06-07 8:49 ` Viresh Kumar
2018-06-07 9:09 ` Daniel Lezcano
2018-06-07 9:32 ` Peter Zijlstra
2018-06-07 9:39 ` Peter Zijlstra
2018-06-07 12:31 ` Daniel Lezcano
2018-06-07 9:09 ` Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=57d769f8-46ea-512e-8f89-a0439c9d053f@linaro.org \
--to=daniel.lezcano@linaro.org \
--cc=daniel.thompson@linaro.org \
--cc=edubezval@gmail.com \
--cc=javi.merino@kernel.org \
--cc=kevin.wangtao@linaro.org \
--cc=leo.yan@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=rjw@rjwysocki.net \
--cc=rui.zhang@intel.com \
--cc=vincent.guittot@linaro.org \
--cc=viresh.kumar@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®