Skip to content
  • Srivatsa S. Bhat's avatar
    cpufreq: Catch double invocations of cpufreq_freq_transition_begin/end · ca654dc3
    Srivatsa S. Bhat authored
    
    
    Some cpufreq drivers were redundantly invoking the _begin() and _end()
    APIs around frequency transitions, and this double invocation (one from
    the cpufreq core and the other from the cpufreq driver) used to result
    in a self-deadlock, leading to system hangs during boot. (The _begin()
    API makes contending callers wait until the previous invocation is
    complete. Hence, the cpufreq driver would end up waiting on itself!).
    
    Now all such drivers have been fixed, but debugging this issue was not
    very straight-forward (even lockdep didn't catch this). So let us add a
    debug infrastructure to the cpufreq core to catch such issues more easily
    in the future.
    
    We add a new field called 'transition_task' to the policy structure, to keep
    track of the task which is performing the frequency transition. Using this
    field, we make note of this task during _begin() and print a warning if we
    find a case where the same task is calling _begin() again, before completing
    the previous frequency transition using the corresponding _end().
    
    We have left out ASYNC_NOTIFICATION drivers from this debug infrastructure
    for 2 reasons:
    
    1. At the moment, we have no way to avoid a particular scenario where this
       debug infrastructure can emit false-positive warnings for such drivers.
       The scenario is depicted below:
    
             Task A						Task B
    
        /* 1st freq transition */
        Invoke _begin() {
                ...
                ...
        }
    
        Change the frequency
    
        /* 2nd freq transition */
        Invoke _begin() {
    	    ...	//waiting for B to
                ... //finish _end() for
    	    ... //the 1st transition
    	    ...	      |				Got interrupt for successful
    	    ...	      |				change of frequency (1st one).
    	    ...       |
    	    ...	      |				/* 1st freq transition */
    	    ...	      |				Invoke _end() {
    	    ...	      |					...
    	    ...	      V				}
    	    ...
    	    ...
        }
    
       This scenario is actually deadlock-free because, once Task A changes the
       frequency, it is Task B's responsibility to invoke the corresponding
       _end() for the 1st frequency transition. Hence it is perfectly legal for
       Task A to go ahead and attempt another frequency transition in the meantime.
       (Of course it won't be able to proceed until Task B finishes the 1st _end(),
       but this doesn't cause a deadlock or a hang).
    
       The debug infrastructure cannot handle this scenario and will treat it as
       a deadlock and print a warning. To avoid this, we exclude such drivers
       from the purview of this code.
    
    2. Luckily, we don't _need_ this infrastructure for ASYNC_NOTIFICATION drivers
       at all! The cpufreq core does not automatically invoke the _begin() and
       _end() APIs during frequency transitions in such drivers. Thus, the driver
       alone is responsible for invoking _begin()/_end() and hence there shouldn't
       be any conflicts which lead to double invocations. So, we can skip these
       drivers, since the probability that such drivers will hit this problem is
       extremely low, as outlined above.
    
    Signed-off-by: default avatarSrivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
    Acked-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
    Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
    ca654dc3