diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2022-06-30 15:30:30 +0200 | 
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2022-06-30 15:30:30 +0200 | 
| commit | 589cb2c0b8daf4fd03e7012c00a52804769e7699 (patch) | |
| tree | dacfcf99c51d8fec0f804d19c6c0807e7f909917 | |
| parent | 03c765b0e3b4cb5063276b086c76f7a612856a9a (diff) | |
| parent | f08fe6fcbe13f83558ecccc4acaf5af3dce71a1f (diff) | |
Merge tag 'devfreq-fixes-for-5.19-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux
Pull devfreq fixes for 5.19-rc5 from Chanwoo Choi:
"1. Fix devfreq passive governor issue when cpufreq policies are not
    ready during kernel boot because some CPUs turn on after kernel
    booting or others.
    - Re-initialize the vairables of struct devfreq_passive_data when
      PROBE_DEFER happens when cpufreq_get() returns NULL.
    - Use dev_err_probe to mute warning when PROBE_DEFER.
    - Fix cpufreq passive unregister erroring on PROBE_DEFER
      by using the allocated parent_cpu_data list to free resouce
      instead of for_each_possible_cpu().
    - Remove duplicate cpufreq passive unregister and warning when
      PROBE_DEFER.
    - Use HZ_PER_KZH macro in units.h.
    - Fix wrong indentation in SPDX-License line.
 2. Fix reference count leak in exynos-ppmu.c by using of_node_put().
 3. Rework freq_table to be local to devfreq struct
    - struct devfreq_dev_profile includes freq_table array to store
      the supported frequencies. If devfreq driver doesn't initialize
      the freq_table, devfreq core allocates the memory and initializes
      the freq_table.
      On a devfreq PROBE_DEFER, the freq_table in the driver profile
      struct is never reset and may be left in an undefined state. To fix
      this and correctly handle PROBE_DEFER, use a local freq_table and
      max_state in the devfreq struct."
* tag 'devfreq-fixes-for-5.19-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux:
  PM / devfreq: passive: revert an editing accident in SPDX-License line
  PM / devfreq: Fix kernel warning with cpufreq passive register fail
  PM / devfreq: Rework freq_table to be local to devfreq struct
  PM / devfreq: exynos-ppmu: Fix refcount leak in of_get_devfreq_events
  PM / devfreq: passive: Use HZ_PER_KHZ macro in units.h
  PM / devfreq: Fix cpufreq passive unregister erroring on PROBE_DEFER
  PM / devfreq: Mute warning on governor PROBE_DEFER
  PM / devfreq: Fix kernel panic with cpu based scaling to passive gov
| -rw-r--r-- | drivers/devfreq/devfreq.c | 76 | ||||
| -rw-r--r-- | drivers/devfreq/event/exynos-ppmu.c | 8 | ||||
| -rw-r--r-- | drivers/devfreq/governor_passive.c | 62 | ||||
| -rw-r--r-- | include/linux/devfreq.h | 5 | 
4 files changed, 75 insertions, 76 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 01474daf4548..9602141bb8ec 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -123,7 +123,7 @@ void devfreq_get_freq_range(struct devfreq *devfreq,  			    unsigned long *min_freq,  			    unsigned long *max_freq)  { -	unsigned long *freq_table = devfreq->profile->freq_table; +	unsigned long *freq_table = devfreq->freq_table;  	s32 qos_min_freq, qos_max_freq;  	lockdep_assert_held(&devfreq->lock); @@ -133,11 +133,11 @@ void devfreq_get_freq_range(struct devfreq *devfreq,  	 * The devfreq drivers can initialize this in either ascending or  	 * descending order and devfreq core supports both.  	 */ -	if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) { +	if (freq_table[0] < freq_table[devfreq->max_state - 1]) {  		*min_freq = freq_table[0]; -		*max_freq = freq_table[devfreq->profile->max_state - 1]; +		*max_freq = freq_table[devfreq->max_state - 1];  	} else { -		*min_freq = freq_table[devfreq->profile->max_state - 1]; +		*min_freq = freq_table[devfreq->max_state - 1];  		*max_freq = freq_table[0];  	} @@ -169,8 +169,8 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)  {  	int lev; -	for (lev = 0; lev < devfreq->profile->max_state; lev++) -		if (freq == devfreq->profile->freq_table[lev]) +	for (lev = 0; lev < devfreq->max_state; lev++) +		if (freq == devfreq->freq_table[lev])  			return lev;  	return -EINVAL; @@ -178,7 +178,6 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)  static int set_freq_table(struct devfreq *devfreq)  { -	struct devfreq_dev_profile *profile = devfreq->profile;  	struct dev_pm_opp *opp;  	unsigned long freq;  	int i, count; @@ -188,25 +187,22 @@ static int set_freq_table(struct devfreq *devfreq)  	if (count <= 0)  		return -EINVAL; -	profile->max_state = count; -	profile->freq_table = devm_kcalloc(devfreq->dev.parent, -					profile->max_state, -					sizeof(*profile->freq_table), -					GFP_KERNEL); -	if (!profile->freq_table) { -		profile->max_state = 0; +	devfreq->max_state = count; +	devfreq->freq_table = devm_kcalloc(devfreq->dev.parent, +					   devfreq->max_state, +					   sizeof(*devfreq->freq_table), +					   GFP_KERNEL); +	if (!devfreq->freq_table)  		return -ENOMEM; -	} -	for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { +	for (i = 0, freq = 0; i < devfreq->max_state; i++, freq++) {  		opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq);  		if (IS_ERR(opp)) { -			devm_kfree(devfreq->dev.parent, profile->freq_table); -			profile->max_state = 0; +			devm_kfree(devfreq->dev.parent, devfreq->freq_table);  			return PTR_ERR(opp);  		}  		dev_pm_opp_put(opp); -		profile->freq_table[i] = freq; +		devfreq->freq_table[i] = freq;  	}  	return 0; @@ -246,7 +242,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)  	if (lev != prev_lev) {  		devfreq->stats.trans_table[ -			(prev_lev * devfreq->profile->max_state) + lev]++; +			(prev_lev * devfreq->max_state) + lev]++;  		devfreq->stats.total_trans++;  	} @@ -835,6 +831,9 @@ struct devfreq *devfreq_add_device(struct device *dev,  		if (err < 0)  			goto err_dev;  		mutex_lock(&devfreq->lock); +	} else { +		devfreq->freq_table = devfreq->profile->freq_table; +		devfreq->max_state = devfreq->profile->max_state;  	}  	devfreq->scaling_min_freq = find_available_min_freq(devfreq); @@ -870,8 +869,8 @@ struct devfreq *devfreq_add_device(struct device *dev,  	devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev,  			array3_size(sizeof(unsigned int), -				    devfreq->profile->max_state, -				    devfreq->profile->max_state), +				    devfreq->max_state, +				    devfreq->max_state),  			GFP_KERNEL);  	if (!devfreq->stats.trans_table) {  		mutex_unlock(&devfreq->lock); @@ -880,7 +879,7 @@ struct devfreq *devfreq_add_device(struct device *dev,  	}  	devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev, -			devfreq->profile->max_state, +			devfreq->max_state,  			sizeof(*devfreq->stats.time_in_state),  			GFP_KERNEL);  	if (!devfreq->stats.time_in_state) { @@ -932,8 +931,9 @@ struct devfreq *devfreq_add_device(struct device *dev,  	err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START,  						NULL);  	if (err) { -		dev_err(dev, "%s: Unable to start governor for the device\n", -			__func__); +		dev_err_probe(dev, err, +			"%s: Unable to start governor for the device\n", +			 __func__);  		goto err_init;  	}  	create_sysfs_files(devfreq, devfreq->governor); @@ -1665,9 +1665,9 @@ static ssize_t available_frequencies_show(struct device *d,  	mutex_lock(&df->lock); -	for (i = 0; i < df->profile->max_state; i++) +	for (i = 0; i < df->max_state; i++)  		count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), -				"%lu ", df->profile->freq_table[i]); +				"%lu ", df->freq_table[i]);  	mutex_unlock(&df->lock);  	/* Truncate the trailing space */ @@ -1690,7 +1690,7 @@ static ssize_t trans_stat_show(struct device *dev,  	if (!df->profile)  		return -EINVAL; -	max_state = df->profile->max_state; +	max_state = df->max_state;  	if (max_state == 0)  		return sprintf(buf, "Not Supported.\n"); @@ -1707,19 +1707,17 @@ static ssize_t trans_stat_show(struct device *dev,  	len += sprintf(buf + len, "           :");  	for (i = 0; i < max_state; i++)  		len += sprintf(buf + len, "%10lu", -				df->profile->freq_table[i]); +				df->freq_table[i]);  	len += sprintf(buf + len, "   time(ms)\n");  	for (i = 0; i < max_state; i++) { -		if (df->profile->freq_table[i] -					== df->previous_freq) { +		if (df->freq_table[i] == df->previous_freq)  			len += sprintf(buf + len, "*"); -		} else { +		else  			len += sprintf(buf + len, " "); -		} -		len += sprintf(buf + len, "%10lu:", -				df->profile->freq_table[i]); + +		len += sprintf(buf + len, "%10lu:", df->freq_table[i]);  		for (j = 0; j < max_state; j++)  			len += sprintf(buf + len, "%10u",  				df->stats.trans_table[(i * max_state) + j]); @@ -1743,7 +1741,7 @@ static ssize_t trans_stat_store(struct device *dev,  	if (!df->profile)  		return -EINVAL; -	if (df->profile->max_state == 0) +	if (df->max_state == 0)  		return count;  	err = kstrtoint(buf, 10, &value); @@ -1751,11 +1749,11 @@ static ssize_t trans_stat_store(struct device *dev,  		return -EINVAL;  	mutex_lock(&df->lock); -	memset(df->stats.time_in_state, 0, (df->profile->max_state * +	memset(df->stats.time_in_state, 0, (df->max_state *  					sizeof(*df->stats.time_in_state)));  	memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int), -					df->profile->max_state, -					df->profile->max_state)); +					df->max_state, +					df->max_state));  	df->stats.total_trans = 0;  	df->stats.last_update = get_jiffies_64();  	mutex_unlock(&df->lock); diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c index 9b849d781116..a443e7c42daf 100644 --- a/drivers/devfreq/event/exynos-ppmu.c +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -519,15 +519,19 @@ static int of_get_devfreq_events(struct device_node *np,  	count = of_get_child_count(events_np);  	desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL); -	if (!desc) +	if (!desc) { +		of_node_put(events_np);  		return -ENOMEM; +	}  	info->num_events = count;  	of_id = of_match_device(exynos_ppmu_id_match, dev);  	if (of_id)  		info->ppmu_type = (enum exynos_ppmu_type)of_id->data; -	else +	else { +		of_node_put(events_np);  		return -EINVAL; +	}  	j = 0;  	for_each_child_of_node(events_np, node) { diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c index 72c67979ebe1..953cf9a1e9f7 100644 --- a/drivers/devfreq/governor_passive.c +++ b/drivers/devfreq/governor_passive.c @@ -1,4 +1,4 @@ -	// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0-only  /*   * linux/drivers/devfreq/governor_passive.c   * @@ -14,10 +14,9 @@  #include <linux/slab.h>  #include <linux/device.h>  #include <linux/devfreq.h> +#include <linux/units.h>  #include "governor.h" -#define HZ_PER_KHZ	1000 -  static struct devfreq_cpu_data *  get_parent_cpu_data(struct devfreq_passive_data *p_data,  		    struct cpufreq_policy *policy) @@ -34,6 +33,20 @@ get_parent_cpu_data(struct devfreq_passive_data *p_data,  	return NULL;  } +static void delete_parent_cpu_data(struct devfreq_passive_data *p_data) +{ +	struct devfreq_cpu_data *parent_cpu_data, *tmp; + +	list_for_each_entry_safe(parent_cpu_data, tmp, &p_data->cpu_data_list, node) { +		list_del(&parent_cpu_data->node); + +		if (parent_cpu_data->opp_table) +			dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); + +		kfree(parent_cpu_data); +	} +} +  static unsigned long get_target_freq_by_required_opp(struct device *p_dev,  						struct opp_table *p_opp_table,  						struct opp_table *opp_table, @@ -131,18 +144,18 @@ static int get_target_freq_with_devfreq(struct devfreq *devfreq,  		goto out;  	/* Use interpolation if required opps is not available */ -	for (i = 0; i < parent_devfreq->profile->max_state; i++) -		if (parent_devfreq->profile->freq_table[i] == *freq) +	for (i = 0; i < parent_devfreq->max_state; i++) +		if (parent_devfreq->freq_table[i] == *freq)  			break; -	if (i == parent_devfreq->profile->max_state) +	if (i == parent_devfreq->max_state)  		return -EINVAL; -	if (i < devfreq->profile->max_state) { -		child_freq = devfreq->profile->freq_table[i]; +	if (i < devfreq->max_state) { +		child_freq = devfreq->freq_table[i];  	} else { -		count = devfreq->profile->max_state; -		child_freq = devfreq->profile->freq_table[count - 1]; +		count = devfreq->max_state; +		child_freq = devfreq->freq_table[count - 1];  	}  out: @@ -222,8 +235,7 @@ static int cpufreq_passive_unregister_notifier(struct devfreq *devfreq)  {  	struct devfreq_passive_data *p_data  			= (struct devfreq_passive_data *)devfreq->data; -	struct devfreq_cpu_data *parent_cpu_data; -	int cpu, ret = 0; +	int ret;  	if (p_data->nb.notifier_call) {  		ret = cpufreq_unregister_notifier(&p_data->nb, @@ -232,27 +244,9 @@ static int cpufreq_passive_unregister_notifier(struct devfreq *devfreq)  			return ret;  	} -	for_each_possible_cpu(cpu) { -		struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); -		if (!policy) { -			ret = -EINVAL; -			continue; -		} - -		parent_cpu_data = get_parent_cpu_data(p_data, policy); -		if (!parent_cpu_data) { -			cpufreq_cpu_put(policy); -			continue; -		} +	delete_parent_cpu_data(p_data); -		list_del(&parent_cpu_data->node); -		if (parent_cpu_data->opp_table) -			dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); -		kfree(parent_cpu_data); -		cpufreq_cpu_put(policy); -	} - -	return ret; +	return 0;  }  static int cpufreq_passive_register_notifier(struct devfreq *devfreq) @@ -336,7 +330,6 @@ err_free_cpu_data:  err_put_policy:  	cpufreq_cpu_put(policy);  err: -	WARN_ON(cpufreq_passive_unregister_notifier(devfreq));  	return ret;  } @@ -407,8 +400,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,  	if (!p_data)  		return -EINVAL; -	if (!p_data->this) -		p_data->this = devfreq; +	p_data->this = devfreq;  	switch (event) {  	case DEVFREQ_GOV_START: diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index dc10bee75a72..34aab4dd336c 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -148,6 +148,8 @@ struct devfreq_stats {   *		reevaluate operable frequencies. Devfreq users may use   *		devfreq.nb to the corresponding register notifier call chain.   * @work:	delayed work for load monitoring. + * @freq_table:		current frequency table used by the devfreq driver. + * @max_state:		count of entry present in the frequency table.   * @previous_freq:	previously configured frequency value.   * @last_status:	devfreq user device info, performance statistics   * @data:	Private data of the governor. The devfreq framework does not @@ -185,6 +187,9 @@ struct devfreq {  	struct notifier_block nb;  	struct delayed_work work; +	unsigned long *freq_table; +	unsigned int max_state; +  	unsigned long previous_freq;  	struct devfreq_dev_status last_status;  | 
