All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kamil Konieczny <k.konieczny@samsung.com>
To: k.konieczny@samsung.com
Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>,
	Chanwoo Choi <cw00.choi@samsung.com>,
	Kamil Konieczny <k.konieczny@partner.samsung.com>,
	Krzysztof Kozlowski <krzk@kernel.org>,
	Kukjin Kim <kgene@kernel.org>,
	Kyungmin Park <kyungmin.park@samsung.com>,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org,
	linux-samsung-soc@vger.kernel.org,
	Marek Szyprowski <m.szyprowski@samsung.com>,
	MyungJoo Ham <myungjoo.ham@samsung.com>
Subject: [PATCH 7/7] devfreq: move statistics to separate struct
Date: Wed, 13 Nov 2019 10:13:36 +0100	[thread overview]
Message-ID: <20191113091336.5218-8-k.konieczny@samsung.com> (raw)
In-Reply-To: <20191113091336.5218-1-k.konieczny@samsung.com>

Count time and transitions between devfreq frequencies in separate struct
for improved code readability and maintenance.

Signed-off-by: Kamil Konieczny <k.konieczny@samsung.com>
---
 drivers/devfreq/devfreq.c          | 156 ++++++++++++++++-------------
 drivers/devfreq/exynos-bus.c       |   6 +-
 drivers/devfreq/governor_passive.c |  26 +++--
 include/linux/devfreq.h            |  43 ++++----
 4 files changed, 129 insertions(+), 102 deletions(-)

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index d79412b0de59..d85867a91230 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -105,10 +105,11 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq)
  */
 static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
 {
+	struct devfreq_stats *stats = devfreq->profile->stats;
 	int lev;
 
-	for (lev = 0; lev < devfreq->profile->max_state; lev++)
-		if (freq == devfreq->profile->freq_table[lev])
+	for (lev = 0; lev < stats->max_state; lev++)
+		if (freq == stats->freq_table[lev])
 			return lev;
 
 	return -EINVAL;
@@ -117,56 +118,64 @@ 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 devfreq_stats *stats;
 	struct dev_pm_opp *opp;
 	unsigned long freq;
-	int i, count;
+	int i, count, err = -ENOMEM;
 
 	/* Initialize the freq_table from OPP table */
 	count = dev_pm_opp_get_opp_count(devfreq->dev.parent);
 	if (count <= 0)
 		return -EINVAL;
 
-	profile->max_state = count;
-	profile->freq_table = devm_kcalloc(devfreq->dev.parent,
-					count,
-					sizeof(*profile->freq_table),
-					GFP_KERNEL);
-	if (!profile->freq_table) {
-		profile->max_state = 0;
+	stats = devm_kzalloc(devfreq->dev.parent,
+			     sizeof(struct devfreq_stats), GFP_KERNEL);
+	if (!stats)
 		return -ENOMEM;
-	}
 
-	for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
+	profile->stats = stats;
+	stats->max_state = count;
+	stats->freq_table = devm_kcalloc(devfreq->dev.parent,
+					 count,
+					 sizeof(*stats->freq_table),
+					 GFP_KERNEL);
+	if (!stats->freq_table)
+		goto err_no_mem;
+
+	for (i = 0, freq = 0; i < count; 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;
-			return PTR_ERR(opp);
+			devm_kfree(devfreq->dev.parent, stats->freq_table);
+			stats->max_state = 0;
+			err = PTR_ERR(opp);
+			goto err_no_mem;
 		}
 		dev_pm_opp_put(opp);
-		profile->freq_table[i] = freq;
+		stats->freq_table[i] = freq;
 	}
 
-	profile->trans_table = devm_kzalloc(devfreq->dev.parent,
-					    array3_size(sizeof(unsigned int),
-							count, count),
-					    GFP_KERNEL);
-	if (!profile->trans_table)
+	stats->trans_table = devm_kzalloc(devfreq->dev.parent,
+					  array3_size(sizeof(unsigned int),
+						      count, count),
+					  GFP_KERNEL);
+	if (!stats->trans_table)
 		goto err_no_mem;
 
-	profile->time_in_state = devm_kcalloc(devfreq->dev.parent, count,
-					      sizeof(*profile->time_in_state),
-					      GFP_KERNEL);
-	if (!profile->time_in_state)
+	stats->time_in_state = devm_kcalloc(devfreq->dev.parent, count,
+					    sizeof(*stats->time_in_state),
+					    GFP_KERNEL);
+	if (!stats->time_in_state)
 		goto err_no_mem;
 
-	profile->last_time = get_jiffies_64();
-	spin_lock_init(&profile->stats_lock);
+	stats->last_time = get_jiffies_64();
+	spin_lock_init(&stats->stats_lock);
 
 	return 0;
 err_no_mem:
-	profile->max_state = 0;
-	return -ENOMEM;
+	stats->max_state = 0;
+	devm_kfree(devfreq->dev.parent, profile->stats);
+	profile->stats = NULL;
+	return err;
 }
 
 /**
@@ -176,7 +185,7 @@ static int set_freq_table(struct devfreq *devfreq)
  */
 int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 {
-	struct devfreq_dev_profile *profile = devfreq->profile;
+	struct devfreq_stats *stats = devfreq->profile->stats;
 	unsigned long long cur_time;
 	int lev, prev_lev, ret = 0;
 
@@ -184,22 +193,21 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 
 	/* Immediately exit if previous_freq is not initialized yet. */
 	if (!devfreq->previous_freq) {
-		spin_lock(&profile->stats_lock);
-		profile->last_time = cur_time;
-		spin_unlock(&profile->stats_lock);
+		spin_lock(&stats->stats_lock);
+		stats->last_time = cur_time;
+		spin_unlock(&stats->stats_lock);
 		return 0;
 	}
 
 	prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
 
-	spin_lock(&profile->stats_lock);
+	spin_lock(&stats->stats_lock);
 	if (prev_lev < 0) {
 		ret = prev_lev;
 		goto out;
 	}
 
-	profile->time_in_state[prev_lev] +=
-			 cur_time - profile->last_time;
+	stats->time_in_state[prev_lev] += cur_time - stats->last_time;
 	lev = devfreq_get_freq_level(devfreq, freq);
 	if (lev < 0) {
 		ret = lev;
@@ -207,14 +215,14 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 	}
 
 	if (lev != prev_lev) {
-		profile->trans_table[(prev_lev *
-				profile->max_state) + lev]++;
-		profile->total_trans++;
+		stats->trans_table[(prev_lev *
+				stats->max_state) + lev]++;
+		stats->total_trans++;
 	}
 
 out:
-	profile->last_time = cur_time;
-	spin_unlock(&profile->stats_lock);
+	stats->last_time = cur_time;
+	spin_unlock(&stats->stats_lock);
 	return ret;
 }
 EXPORT_SYMBOL(devfreq_update_status);
@@ -504,9 +512,9 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
 		queue_delayed_work(devfreq_wq, &devfreq->work,
 			msecs_to_jiffies(profile->polling_ms));
 
-	spin_lock(&profile->stats_lock);
-	profile->last_time = get_jiffies_64();
-	spin_unlock(&profile->stats_lock);
+	spin_lock(&profile->stats->stats_lock);
+	profile->stats->last_time = get_jiffies_64();
+	spin_unlock(&profile->stats->stats_lock);
 	devfreq->stop_polling = false;
 
 	if (profile->get_cur_freq &&
@@ -677,7 +685,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
 	devfreq->data = data;
 	devfreq->nb.notifier_call = devfreq_notifier_call;
 
-	if (!profile->max_state && !profile->freq_table) {
+	if (!profile->stats) {
 		mutex_unlock(&devfreq->lock);
 		err = set_freq_table(devfreq);
 		if (err < 0)
@@ -1282,6 +1290,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
 			      const char *buf, size_t count)
 {
 	struct devfreq *df = to_devfreq(dev);
+	struct devfreq_stats *stats = df->profile->stats;
 	unsigned long value;
 	int ret;
 
@@ -1297,13 +1306,13 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
 			goto unlock;
 		}
 	} else {
-		unsigned long *freq_table = df->profile->freq_table;
+		unsigned long *freq_table = stats->freq_table;
 
 		/* Get minimum frequency according to sorting order */
-		if (freq_table[0] < freq_table[df->profile->max_state - 1])
+		if (freq_table[0] < freq_table[stats->max_state - 1])
 			value = freq_table[0];
 		else
-			value = freq_table[df->profile->max_state - 1];
+			value = freq_table[stats->max_state - 1];
 	}
 
 	df->min_freq = value;
@@ -1326,6 +1335,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
 			      const char *buf, size_t count)
 {
 	struct devfreq *df = to_devfreq(dev);
+	struct devfreq_stats *stats = df->profile->stats;
 	unsigned long value;
 	int ret;
 
@@ -1341,11 +1351,11 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
 			goto unlock;
 		}
 	} else {
-		unsigned long *freq_table = df->profile->freq_table;
+		unsigned long *freq_table = stats->freq_table;
 
 		/* Get maximum frequency according to sorting order */
-		if (freq_table[0] < freq_table[df->profile->max_state - 1])
-			value = freq_table[df->profile->max_state - 1];
+		if (freq_table[0] < freq_table[stats->max_state - 1])
+			value = freq_table[stats->max_state - 1];
 		else
 			value = freq_table[0];
 	}
@@ -1373,14 +1383,15 @@ static ssize_t available_frequencies_show(struct device *d,
 					  char *buf)
 {
 	struct devfreq *df = to_devfreq(d);
+	struct devfreq_stats *stats = df->profile->stats;
 	ssize_t count = 0;
 	int i;
 
 	mutex_lock(&df->lock);
 
-	for (i = 0; i < df->profile->max_state; i++)
+	for (i = 0; i < stats->max_state; i++)
 		count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
-				"%lu ", df->profile->freq_table[i]);
+				"%lu ", stats->freq_table[i]);
 
 	mutex_unlock(&df->lock);
 	/* Truncate the trailing space */
@@ -1398,9 +1409,10 @@ static ssize_t trans_stat_show(struct device *dev,
 {
 	struct devfreq *devfreq = to_devfreq(dev);
 	struct devfreq_dev_profile *profile = devfreq->profile;
+	struct devfreq_stats *stats = profile->stats;
+	unsigned int max_state = stats->max_state;
 	ssize_t len;
 	int i, j;
-	unsigned int max_state = profile->max_state;
 
 	if (!devfreq->stop_polling &&
 			devfreq_update_status(devfreq, devfreq->previous_freq))
@@ -1411,45 +1423,45 @@ static ssize_t trans_stat_show(struct device *dev,
 	len = sprintf(buf, "     From  :   To\n");
 	len += sprintf(buf + len, "           :");
 
-	spin_lock(&profile->stats_lock);
+	spin_lock(&stats->stats_lock);
 	for (i = 0; i < max_state; i++)
 		len += sprintf(buf + len, "%10lu",
-				profile->freq_table[i]);
+				stats->freq_table[i]);
 
 	len += sprintf(buf + len, "   time(ms)\n");
 
 	for (i = 0; i < max_state; i++) {
-		if (profile->freq_table[i] == devfreq->previous_freq)
+		if (stats->freq_table[i] == devfreq->previous_freq)
 			len += sprintf(buf + len, "*");
 		else
 			len += sprintf(buf + len, " ");
 
 		len += sprintf(buf + len, "%10lu:",
-				profile->freq_table[i]);
+				stats->freq_table[i]);
 		for (j = 0; j < max_state; j++)
 			len += sprintf(buf + len, "%10u",
-				profile->trans_table[(i * max_state) + j]);
+				stats->trans_table[(i * max_state) + j]);
 		len += sprintf(buf + len, "%10llu\n", (u64)
-			jiffies64_to_msecs(profile->time_in_state[i]));
+			jiffies64_to_msecs(stats->time_in_state[i]));
 	}
 
 	len += sprintf(buf + len, "Total transition : %u\n",
-					profile->total_trans);
-	spin_unlock(&profile->stats_lock);
+					stats->total_trans);
+	spin_unlock(&stats->stats_lock);
 	return len;
 }
 static DEVICE_ATTR_RO(trans_stat);
 
-static void defvreq_stats_clear_table(struct devfreq_dev_profile *profile)
+static void defvreq_stats_clear_table(struct devfreq_stats *stats)
 {
-	unsigned int count = profile->max_state;
-
-	spin_lock(&profile->stats_lock);
-	memset(profile->time_in_state, 0, count * sizeof(u64));
-	memset(profile->trans_table, 0, count * count * sizeof(int));
-	profile->last_time = get_jiffies_64();
-	profile->total_trans = 0;
-	spin_unlock(&profile->stats_lock);
+	unsigned int count = stats->max_state;
+
+	spin_lock(&stats->stats_lock);
+	memset(stats->time_in_state, 0, count * sizeof(u64));
+	memset(stats->trans_table, 0, count * count * sizeof(int));
+	stats->last_time = get_jiffies_64();
+	stats->total_trans = 0;
+	spin_unlock(&stats->stats_lock);
 }
 
 static ssize_t trans_reset_store(struct device *dev,
@@ -1459,7 +1471,7 @@ static ssize_t trans_reset_store(struct device *dev,
 {
 	struct devfreq *devfreq = to_devfreq(dev);
 
-	defvreq_stats_clear_table(devfreq->profile);
+	defvreq_stats_clear_table(devfreq->profile->stats);
 
 	return count;
 }
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
index d9f377912c10..b212aae2bb3e 100644
--- a/drivers/devfreq/exynos-bus.c
+++ b/drivers/devfreq/exynos-bus.c
@@ -496,9 +496,9 @@ static int exynos_bus_probe(struct platform_device *pdev)
 	}
 
 out:
-	max_state = bus->devfreq->profile->max_state;
-	min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
-	max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);
+	max_state = profile->stats->max_state;
+	min_freq = (profile->stats->freq_table[0] / 1000);
+	max_freq = (profile->stats->freq_table[max_state - 1] / 1000);
 	pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n",
 			dev_name(dev), min_freq, max_freq);
 
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
index 58308948b863..b2d87a88335c 100644
--- a/drivers/devfreq/governor_passive.c
+++ b/drivers/devfreq/governor_passive.c
@@ -18,6 +18,8 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
 	struct devfreq_passive_data *p_data
 			= (struct devfreq_passive_data *)devfreq->data;
 	struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
+	struct devfreq_stats *parent_stats = parent_devfreq->profile->stats;
+	struct devfreq_stats *stats;
 	unsigned long child_freq = ULONG_MAX;
 	struct dev_pm_opp *opp;
 	int i, count, ret = 0;
@@ -47,10 +49,14 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
 	 * device. And then the index is used for getting the suitable
 	 * new frequency for passive devfreq device.
 	 */
-	if (!devfreq->profile || !devfreq->profile->freq_table
-		|| devfreq->profile->max_state <= 0)
+	if (!devfreq->profile || !devfreq->profile->stats ||
+	    devfreq->profile->stats->max_state <= 0 ||
+	    !parent_devfreq->profile ||	!parent_devfreq->profile->stats ||
+	    parent_devfreq->profile->stats->max_state <= 0)
 		return -EINVAL;
 
+	stats = devfreq->profile->stats;
+	parent_stats = parent_devfreq->profile->stats;
 	/*
 	 * The passive governor have to get the correct frequency from OPP
 	 * list of parent device. Because in this case, *freq is temporary
@@ -68,21 +74,21 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
 	 * Get the OPP table's index of decided freqeuncy by governor
 	 * of parent device.
 	 */
-	for (i = 0; i < parent_devfreq->profile->max_state; i++)
-		if (parent_devfreq->profile->freq_table[i] == *freq)
+	for (i = 0; i < parent_stats->max_state; i++)
+		if (parent_stats->freq_table[i] == *freq)
 			break;
 
-	if (i == parent_devfreq->profile->max_state) {
+	if (i == parent_stats->max_state) {
 		ret = -EINVAL;
 		goto out;
 	}
 
 	/* Get the suitable frequency by using index of parent device. */
-	if (i < devfreq->profile->max_state) {
-		child_freq = devfreq->profile->freq_table[i];
+	if (i < stats->max_state) {
+		child_freq = stats->freq_table[i];
 	} else {
-		count = devfreq->profile->max_state;
-		child_freq = devfreq->profile->freq_table[count - 1];
+		count = stats->max_state;
+		child_freq = stats->freq_table[count - 1];
 	}
 
 	/* Return the suitable frequency for passive device. */
@@ -109,7 +115,7 @@ static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
 	if (ret < 0)
 		goto out;
 
-	if (devfreq->profile->freq_table
+	if (devfreq->profile->stats
 		&& (devfreq_update_status(devfreq, freq)))
 		dev_err(&devfreq->dev,
 			"Couldn't update frequency transition information.\n");
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 4ceb2a517a9c..8459af1a1583 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -64,6 +64,30 @@ struct devfreq_dev_status {
  */
 #define DEVFREQ_FLAG_LEAST_UPPER_BOUND		0x1
 
+/**
+ * struct devfreq_stats - Devfreq's transitions stats counters
+ * @freq_table:		Optional list of frequencies to support statistics
+ *			and freq_table must be generated in ascending order.
+ * @max_state:		The size of freq_table.
+ * @total_trans:	Number of devfreq transitions
+ * @trans_table:	Statistics of devfreq transitions
+ * @time_in_state:	Statistics of devfreq states
+ * @last_time:		The last time stats were updated
+ * @stats_lock:		Lock protecting trans_table, time_in_state,
+ *			last_time and total_trans used for statistics
+ */
+struct devfreq_stats {
+	unsigned long *freq_table;
+	unsigned int max_state;
+
+	/* information for device frequency transition */
+	unsigned int total_trans;
+	unsigned int *trans_table;
+	u64 *time_in_state;
+	unsigned long long last_time;
+	spinlock_t stats_lock;
+};
+
 /**
  * struct devfreq_dev_profile - Devfreq's user device profile
  * @initial_freq:	The operating frequency when devfreq_add_device() is
@@ -88,15 +112,7 @@ struct devfreq_dev_status {
  *			from devfreq_remove_device() call. If the user
  *			has registered devfreq->nb at a notifier-head,
  *			this is the time to unregister it.
- * @freq_table:		Optional list of frequencies to support statistics
- *			and freq_table must be generated in ascending order.
- * @max_state:		The size of freq_table.
- * @total_trans:	Number of devfreq transitions
- * @trans_table:	Statistics of devfreq transitions
- * @time_in_state:	Statistics of devfreq states
- * @last_time:		The last time stats were updated
- * @stats_lock:		Lock protecting trans_table, time_in_state,
- *			last_time and total_trans used for statistics
+ * @stats:		Statistics of devfreq states and state transitions
  */
 struct devfreq_dev_profile {
 	unsigned long initial_freq;
@@ -108,14 +124,7 @@ struct devfreq_dev_profile {
 	int (*get_cur_freq)(struct device *dev, unsigned long *freq);
 	void (*exit)(struct device *dev);
 
-	unsigned long *freq_table;
-	unsigned int max_state;
-	/* information for device frequency transition */
-	unsigned int total_trans;
-	unsigned int *trans_table;
-	u64 *time_in_state;
-	unsigned long long last_time;
-	spinlock_t stats_lock;
+	struct devfreq_stats *stats;
 };
 
 /**
-- 
2.24.0


WARNING: multiple messages have this Message-ID (diff)
From: Kamil Konieczny <k.konieczny@samsung.com>
To: k.konieczny@samsung.com
Cc: linux-samsung-soc@vger.kernel.org,
	Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>,
	linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org,
	Krzysztof Kozlowski <krzk@kernel.org>,
	Chanwoo Choi <cw00.choi@samsung.com>,
	Kyungmin Park <kyungmin.park@samsung.com>,
	Kukjin Kim <kgene@kernel.org>,
	MyungJoo Ham <myungjoo.ham@samsung.com>,
	Kamil Konieczny <k.konieczny@partner.samsung.com>,
	linux-arm-kernel@lists.infradead.org,
	Marek Szyprowski <m.szyprowski@samsung.com>
Subject: [PATCH 7/7] devfreq: move statistics to separate struct
Date: Wed, 13 Nov 2019 10:13:36 +0100	[thread overview]
Message-ID: <20191113091336.5218-8-k.konieczny@samsung.com> (raw)
In-Reply-To: <20191113091336.5218-1-k.konieczny@samsung.com>

Count time and transitions between devfreq frequencies in separate struct
for improved code readability and maintenance.

Signed-off-by: Kamil Konieczny <k.konieczny@samsung.com>
---
 drivers/devfreq/devfreq.c          | 156 ++++++++++++++++-------------
 drivers/devfreq/exynos-bus.c       |   6 +-
 drivers/devfreq/governor_passive.c |  26 +++--
 include/linux/devfreq.h            |  43 ++++----
 4 files changed, 129 insertions(+), 102 deletions(-)

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index d79412b0de59..d85867a91230 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -105,10 +105,11 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq)
  */
 static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
 {
+	struct devfreq_stats *stats = devfreq->profile->stats;
 	int lev;
 
-	for (lev = 0; lev < devfreq->profile->max_state; lev++)
-		if (freq == devfreq->profile->freq_table[lev])
+	for (lev = 0; lev < stats->max_state; lev++)
+		if (freq == stats->freq_table[lev])
 			return lev;
 
 	return -EINVAL;
@@ -117,56 +118,64 @@ 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 devfreq_stats *stats;
 	struct dev_pm_opp *opp;
 	unsigned long freq;
-	int i, count;
+	int i, count, err = -ENOMEM;
 
 	/* Initialize the freq_table from OPP table */
 	count = dev_pm_opp_get_opp_count(devfreq->dev.parent);
 	if (count <= 0)
 		return -EINVAL;
 
-	profile->max_state = count;
-	profile->freq_table = devm_kcalloc(devfreq->dev.parent,
-					count,
-					sizeof(*profile->freq_table),
-					GFP_KERNEL);
-	if (!profile->freq_table) {
-		profile->max_state = 0;
+	stats = devm_kzalloc(devfreq->dev.parent,
+			     sizeof(struct devfreq_stats), GFP_KERNEL);
+	if (!stats)
 		return -ENOMEM;
-	}
 
-	for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
+	profile->stats = stats;
+	stats->max_state = count;
+	stats->freq_table = devm_kcalloc(devfreq->dev.parent,
+					 count,
+					 sizeof(*stats->freq_table),
+					 GFP_KERNEL);
+	if (!stats->freq_table)
+		goto err_no_mem;
+
+	for (i = 0, freq = 0; i < count; 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;
-			return PTR_ERR(opp);
+			devm_kfree(devfreq->dev.parent, stats->freq_table);
+			stats->max_state = 0;
+			err = PTR_ERR(opp);
+			goto err_no_mem;
 		}
 		dev_pm_opp_put(opp);
-		profile->freq_table[i] = freq;
+		stats->freq_table[i] = freq;
 	}
 
-	profile->trans_table = devm_kzalloc(devfreq->dev.parent,
-					    array3_size(sizeof(unsigned int),
-							count, count),
-					    GFP_KERNEL);
-	if (!profile->trans_table)
+	stats->trans_table = devm_kzalloc(devfreq->dev.parent,
+					  array3_size(sizeof(unsigned int),
+						      count, count),
+					  GFP_KERNEL);
+	if (!stats->trans_table)
 		goto err_no_mem;
 
-	profile->time_in_state = devm_kcalloc(devfreq->dev.parent, count,
-					      sizeof(*profile->time_in_state),
-					      GFP_KERNEL);
-	if (!profile->time_in_state)
+	stats->time_in_state = devm_kcalloc(devfreq->dev.parent, count,
+					    sizeof(*stats->time_in_state),
+					    GFP_KERNEL);
+	if (!stats->time_in_state)
 		goto err_no_mem;
 
-	profile->last_time = get_jiffies_64();
-	spin_lock_init(&profile->stats_lock);
+	stats->last_time = get_jiffies_64();
+	spin_lock_init(&stats->stats_lock);
 
 	return 0;
 err_no_mem:
-	profile->max_state = 0;
-	return -ENOMEM;
+	stats->max_state = 0;
+	devm_kfree(devfreq->dev.parent, profile->stats);
+	profile->stats = NULL;
+	return err;
 }
 
 /**
@@ -176,7 +185,7 @@ static int set_freq_table(struct devfreq *devfreq)
  */
 int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 {
-	struct devfreq_dev_profile *profile = devfreq->profile;
+	struct devfreq_stats *stats = devfreq->profile->stats;
 	unsigned long long cur_time;
 	int lev, prev_lev, ret = 0;
 
@@ -184,22 +193,21 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 
 	/* Immediately exit if previous_freq is not initialized yet. */
 	if (!devfreq->previous_freq) {
-		spin_lock(&profile->stats_lock);
-		profile->last_time = cur_time;
-		spin_unlock(&profile->stats_lock);
+		spin_lock(&stats->stats_lock);
+		stats->last_time = cur_time;
+		spin_unlock(&stats->stats_lock);
 		return 0;
 	}
 
 	prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
 
-	spin_lock(&profile->stats_lock);
+	spin_lock(&stats->stats_lock);
 	if (prev_lev < 0) {
 		ret = prev_lev;
 		goto out;
 	}
 
-	profile->time_in_state[prev_lev] +=
-			 cur_time - profile->last_time;
+	stats->time_in_state[prev_lev] += cur_time - stats->last_time;
 	lev = devfreq_get_freq_level(devfreq, freq);
 	if (lev < 0) {
 		ret = lev;
@@ -207,14 +215,14 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 	}
 
 	if (lev != prev_lev) {
-		profile->trans_table[(prev_lev *
-				profile->max_state) + lev]++;
-		profile->total_trans++;
+		stats->trans_table[(prev_lev *
+				stats->max_state) + lev]++;
+		stats->total_trans++;
 	}
 
 out:
-	profile->last_time = cur_time;
-	spin_unlock(&profile->stats_lock);
+	stats->last_time = cur_time;
+	spin_unlock(&stats->stats_lock);
 	return ret;
 }
 EXPORT_SYMBOL(devfreq_update_status);
@@ -504,9 +512,9 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
 		queue_delayed_work(devfreq_wq, &devfreq->work,
 			msecs_to_jiffies(profile->polling_ms));
 
-	spin_lock(&profile->stats_lock);
-	profile->last_time = get_jiffies_64();
-	spin_unlock(&profile->stats_lock);
+	spin_lock(&profile->stats->stats_lock);
+	profile->stats->last_time = get_jiffies_64();
+	spin_unlock(&profile->stats->stats_lock);
 	devfreq->stop_polling = false;
 
 	if (profile->get_cur_freq &&
@@ -677,7 +685,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
 	devfreq->data = data;
 	devfreq->nb.notifier_call = devfreq_notifier_call;
 
-	if (!profile->max_state && !profile->freq_table) {
+	if (!profile->stats) {
 		mutex_unlock(&devfreq->lock);
 		err = set_freq_table(devfreq);
 		if (err < 0)
@@ -1282,6 +1290,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
 			      const char *buf, size_t count)
 {
 	struct devfreq *df = to_devfreq(dev);
+	struct devfreq_stats *stats = df->profile->stats;
 	unsigned long value;
 	int ret;
 
@@ -1297,13 +1306,13 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
 			goto unlock;
 		}
 	} else {
-		unsigned long *freq_table = df->profile->freq_table;
+		unsigned long *freq_table = stats->freq_table;
 
 		/* Get minimum frequency according to sorting order */
-		if (freq_table[0] < freq_table[df->profile->max_state - 1])
+		if (freq_table[0] < freq_table[stats->max_state - 1])
 			value = freq_table[0];
 		else
-			value = freq_table[df->profile->max_state - 1];
+			value = freq_table[stats->max_state - 1];
 	}
 
 	df->min_freq = value;
@@ -1326,6 +1335,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
 			      const char *buf, size_t count)
 {
 	struct devfreq *df = to_devfreq(dev);
+	struct devfreq_stats *stats = df->profile->stats;
 	unsigned long value;
 	int ret;
 
@@ -1341,11 +1351,11 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
 			goto unlock;
 		}
 	} else {
-		unsigned long *freq_table = df->profile->freq_table;
+		unsigned long *freq_table = stats->freq_table;
 
 		/* Get maximum frequency according to sorting order */
-		if (freq_table[0] < freq_table[df->profile->max_state - 1])
-			value = freq_table[df->profile->max_state - 1];
+		if (freq_table[0] < freq_table[stats->max_state - 1])
+			value = freq_table[stats->max_state - 1];
 		else
 			value = freq_table[0];
 	}
@@ -1373,14 +1383,15 @@ static ssize_t available_frequencies_show(struct device *d,
 					  char *buf)
 {
 	struct devfreq *df = to_devfreq(d);
+	struct devfreq_stats *stats = df->profile->stats;
 	ssize_t count = 0;
 	int i;
 
 	mutex_lock(&df->lock);
 
-	for (i = 0; i < df->profile->max_state; i++)
+	for (i = 0; i < stats->max_state; i++)
 		count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
-				"%lu ", df->profile->freq_table[i]);
+				"%lu ", stats->freq_table[i]);
 
 	mutex_unlock(&df->lock);
 	/* Truncate the trailing space */
@@ -1398,9 +1409,10 @@ static ssize_t trans_stat_show(struct device *dev,
 {
 	struct devfreq *devfreq = to_devfreq(dev);
 	struct devfreq_dev_profile *profile = devfreq->profile;
+	struct devfreq_stats *stats = profile->stats;
+	unsigned int max_state = stats->max_state;
 	ssize_t len;
 	int i, j;
-	unsigned int max_state = profile->max_state;
 
 	if (!devfreq->stop_polling &&
 			devfreq_update_status(devfreq, devfreq->previous_freq))
@@ -1411,45 +1423,45 @@ static ssize_t trans_stat_show(struct device *dev,
 	len = sprintf(buf, "     From  :   To\n");
 	len += sprintf(buf + len, "           :");
 
-	spin_lock(&profile->stats_lock);
+	spin_lock(&stats->stats_lock);
 	for (i = 0; i < max_state; i++)
 		len += sprintf(buf + len, "%10lu",
-				profile->freq_table[i]);
+				stats->freq_table[i]);
 
 	len += sprintf(buf + len, "   time(ms)\n");
 
 	for (i = 0; i < max_state; i++) {
-		if (profile->freq_table[i] == devfreq->previous_freq)
+		if (stats->freq_table[i] == devfreq->previous_freq)
 			len += sprintf(buf + len, "*");
 		else
 			len += sprintf(buf + len, " ");
 
 		len += sprintf(buf + len, "%10lu:",
-				profile->freq_table[i]);
+				stats->freq_table[i]);
 		for (j = 0; j < max_state; j++)
 			len += sprintf(buf + len, "%10u",
-				profile->trans_table[(i * max_state) + j]);
+				stats->trans_table[(i * max_state) + j]);
 		len += sprintf(buf + len, "%10llu\n", (u64)
-			jiffies64_to_msecs(profile->time_in_state[i]));
+			jiffies64_to_msecs(stats->time_in_state[i]));
 	}
 
 	len += sprintf(buf + len, "Total transition : %u\n",
-					profile->total_trans);
-	spin_unlock(&profile->stats_lock);
+					stats->total_trans);
+	spin_unlock(&stats->stats_lock);
 	return len;
 }
 static DEVICE_ATTR_RO(trans_stat);
 
-static void defvreq_stats_clear_table(struct devfreq_dev_profile *profile)
+static void defvreq_stats_clear_table(struct devfreq_stats *stats)
 {
-	unsigned int count = profile->max_state;
-
-	spin_lock(&profile->stats_lock);
-	memset(profile->time_in_state, 0, count * sizeof(u64));
-	memset(profile->trans_table, 0, count * count * sizeof(int));
-	profile->last_time = get_jiffies_64();
-	profile->total_trans = 0;
-	spin_unlock(&profile->stats_lock);
+	unsigned int count = stats->max_state;
+
+	spin_lock(&stats->stats_lock);
+	memset(stats->time_in_state, 0, count * sizeof(u64));
+	memset(stats->trans_table, 0, count * count * sizeof(int));
+	stats->last_time = get_jiffies_64();
+	stats->total_trans = 0;
+	spin_unlock(&stats->stats_lock);
 }
 
 static ssize_t trans_reset_store(struct device *dev,
@@ -1459,7 +1471,7 @@ static ssize_t trans_reset_store(struct device *dev,
 {
 	struct devfreq *devfreq = to_devfreq(dev);
 
-	defvreq_stats_clear_table(devfreq->profile);
+	defvreq_stats_clear_table(devfreq->profile->stats);
 
 	return count;
 }
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
index d9f377912c10..b212aae2bb3e 100644
--- a/drivers/devfreq/exynos-bus.c
+++ b/drivers/devfreq/exynos-bus.c
@@ -496,9 +496,9 @@ static int exynos_bus_probe(struct platform_device *pdev)
 	}
 
 out:
-	max_state = bus->devfreq->profile->max_state;
-	min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
-	max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);
+	max_state = profile->stats->max_state;
+	min_freq = (profile->stats->freq_table[0] / 1000);
+	max_freq = (profile->stats->freq_table[max_state - 1] / 1000);
 	pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n",
 			dev_name(dev), min_freq, max_freq);
 
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
index 58308948b863..b2d87a88335c 100644
--- a/drivers/devfreq/governor_passive.c
+++ b/drivers/devfreq/governor_passive.c
@@ -18,6 +18,8 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
 	struct devfreq_passive_data *p_data
 			= (struct devfreq_passive_data *)devfreq->data;
 	struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
+	struct devfreq_stats *parent_stats = parent_devfreq->profile->stats;
+	struct devfreq_stats *stats;
 	unsigned long child_freq = ULONG_MAX;
 	struct dev_pm_opp *opp;
 	int i, count, ret = 0;
@@ -47,10 +49,14 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
 	 * device. And then the index is used for getting the suitable
 	 * new frequency for passive devfreq device.
 	 */
-	if (!devfreq->profile || !devfreq->profile->freq_table
-		|| devfreq->profile->max_state <= 0)
+	if (!devfreq->profile || !devfreq->profile->stats ||
+	    devfreq->profile->stats->max_state <= 0 ||
+	    !parent_devfreq->profile ||	!parent_devfreq->profile->stats ||
+	    parent_devfreq->profile->stats->max_state <= 0)
 		return -EINVAL;
 
+	stats = devfreq->profile->stats;
+	parent_stats = parent_devfreq->profile->stats;
 	/*
 	 * The passive governor have to get the correct frequency from OPP
 	 * list of parent device. Because in this case, *freq is temporary
@@ -68,21 +74,21 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
 	 * Get the OPP table's index of decided freqeuncy by governor
 	 * of parent device.
 	 */
-	for (i = 0; i < parent_devfreq->profile->max_state; i++)
-		if (parent_devfreq->profile->freq_table[i] == *freq)
+	for (i = 0; i < parent_stats->max_state; i++)
+		if (parent_stats->freq_table[i] == *freq)
 			break;
 
-	if (i == parent_devfreq->profile->max_state) {
+	if (i == parent_stats->max_state) {
 		ret = -EINVAL;
 		goto out;
 	}
 
 	/* Get the suitable frequency by using index of parent device. */
-	if (i < devfreq->profile->max_state) {
-		child_freq = devfreq->profile->freq_table[i];
+	if (i < stats->max_state) {
+		child_freq = stats->freq_table[i];
 	} else {
-		count = devfreq->profile->max_state;
-		child_freq = devfreq->profile->freq_table[count - 1];
+		count = stats->max_state;
+		child_freq = stats->freq_table[count - 1];
 	}
 
 	/* Return the suitable frequency for passive device. */
@@ -109,7 +115,7 @@ static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
 	if (ret < 0)
 		goto out;
 
-	if (devfreq->profile->freq_table
+	if (devfreq->profile->stats
 		&& (devfreq_update_status(devfreq, freq)))
 		dev_err(&devfreq->dev,
 			"Couldn't update frequency transition information.\n");
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 4ceb2a517a9c..8459af1a1583 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -64,6 +64,30 @@ struct devfreq_dev_status {
  */
 #define DEVFREQ_FLAG_LEAST_UPPER_BOUND		0x1
 
+/**
+ * struct devfreq_stats - Devfreq's transitions stats counters
+ * @freq_table:		Optional list of frequencies to support statistics
+ *			and freq_table must be generated in ascending order.
+ * @max_state:		The size of freq_table.
+ * @total_trans:	Number of devfreq transitions
+ * @trans_table:	Statistics of devfreq transitions
+ * @time_in_state:	Statistics of devfreq states
+ * @last_time:		The last time stats were updated
+ * @stats_lock:		Lock protecting trans_table, time_in_state,
+ *			last_time and total_trans used for statistics
+ */
+struct devfreq_stats {
+	unsigned long *freq_table;
+	unsigned int max_state;
+
+	/* information for device frequency transition */
+	unsigned int total_trans;
+	unsigned int *trans_table;
+	u64 *time_in_state;
+	unsigned long long last_time;
+	spinlock_t stats_lock;
+};
+
 /**
  * struct devfreq_dev_profile - Devfreq's user device profile
  * @initial_freq:	The operating frequency when devfreq_add_device() is
@@ -88,15 +112,7 @@ struct devfreq_dev_status {
  *			from devfreq_remove_device() call. If the user
  *			has registered devfreq->nb at a notifier-head,
  *			this is the time to unregister it.
- * @freq_table:		Optional list of frequencies to support statistics
- *			and freq_table must be generated in ascending order.
- * @max_state:		The size of freq_table.
- * @total_trans:	Number of devfreq transitions
- * @trans_table:	Statistics of devfreq transitions
- * @time_in_state:	Statistics of devfreq states
- * @last_time:		The last time stats were updated
- * @stats_lock:		Lock protecting trans_table, time_in_state,
- *			last_time and total_trans used for statistics
+ * @stats:		Statistics of devfreq states and state transitions
  */
 struct devfreq_dev_profile {
 	unsigned long initial_freq;
@@ -108,14 +124,7 @@ struct devfreq_dev_profile {
 	int (*get_cur_freq)(struct device *dev, unsigned long *freq);
 	void (*exit)(struct device *dev);
 
-	unsigned long *freq_table;
-	unsigned int max_state;
-	/* information for device frequency transition */
-	unsigned int total_trans;
-	unsigned int *trans_table;
-	u64 *time_in_state;
-	unsigned long long last_time;
-	spinlock_t stats_lock;
+	struct devfreq_stats *stats;
 };
 
 /**
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  parent reply	other threads:[~2019-11-13  9:14 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CGME20191113091350eucas1p2545166dfa1dc3b85aee375e353d7a604@eucas1p2.samsung.com>
2019-11-13  9:13 ` [PATCH 0/7] devfreq: improve devfreq statistics counting Kamil Konieczny
2019-11-13  9:13   ` Kamil Konieczny
     [not found]   ` <CGME20191113091351eucas1p24afdb94f868b6a7a52b43e81462bb674@eucas1p2.samsung.com>
2019-11-13  9:13     ` [PATCH 1/7] devfreq: change time stats to 64-bit Kamil Konieczny
2019-11-13  9:44       ` Chanwoo Choi
     [not found]   ` <CGME20191113091351eucas1p2f83c221ce94fdea695775e00d5215458@eucas1p2.samsung.com>
2019-11-13  9:13     ` [PATCH 2/7] devfreq: protect devfreq stats data with spinlock Kamil Konieczny
2019-11-13  9:47       ` Chanwoo Choi
     [not found]   ` <CGME20191113091352eucas1p2c30c8a73a8362aff872e3cd9312eb24b@eucas1p2.samsung.com>
2019-11-13  9:13     ` [PATCH 3/7] devfreq: add clearing transitions stats in sysfs Kamil Konieczny
2019-11-13  9:41       ` Chanwoo Choi
2019-11-14 18:23         ` Bartlomiej Zolnierkiewicz
2019-11-15  2:47           ` Chanwoo Choi
2019-11-15 14:35             ` Kamil Konieczny
     [not found]   ` <CGME20191113091352eucas1p1825d815661c1a8377449f511c65ea230@eucas1p1.samsung.com>
2019-11-13  9:13     ` [PATCH 4/7] devfreq: change var name used in time statistics Kamil Konieczny
2019-11-13  9:35       ` Chanwoo Choi
     [not found]   ` <CGME20191113091353eucas1p283be3173c7a9ea726b4767f9cb113f0f@eucas1p2.samsung.com>
2019-11-13  9:13     ` [PATCH 5/7] devfreq: move transition statistics to devfreq profile structure Kamil Konieczny
2019-11-14  2:02       ` Chanwoo Choi
2019-11-14 18:10         ` Bartlomiej Zolnierkiewicz
2019-11-15  5:14           ` Chanwoo Choi
2019-11-15 14:39         ` Kamil Konieczny
     [not found]   ` <CGME20191113091353eucas1p2d9f82697e6ec44c0e38225988227c73c@eucas1p2.samsung.com>
2019-11-13  9:13     ` [PATCH 6/7] devfreq: move transition statistics allocations to set_freq_stats() Kamil Konieczny
     [not found]   ` <CGME20191113091354eucas1p265de4985d167814f5080fbdf21b75a0a@eucas1p2.samsung.com>
2019-11-13  9:13     ` Kamil Konieczny [this message]
2019-11-13  9:13       ` [PATCH 7/7] devfreq: move statistics to separate struct Kamil Konieczny
2019-11-14  1:52       ` Chanwoo Choi
2019-11-14  1:52         ` Chanwoo Choi
2019-11-14 18:01         ` Bartlomiej Zolnierkiewicz
2019-11-14 18:01           ` Bartlomiej Zolnierkiewicz
2019-11-15  3:25           ` Chanwoo Choi
2019-11-15  3:25             ` Chanwoo Choi
2019-11-15  6:21             ` Chanwoo Choi
2019-11-15  6:21               ` Chanwoo Choi
2019-11-15 12:40               ` Bartlomiej Zolnierkiewicz
2019-11-15 12:40                 ` Bartlomiej Zolnierkiewicz
2019-12-16 13:01                 ` Lukasz Luba
2019-12-16 13:01                   ` Lukasz Luba
2019-12-17  0:07                   ` Chanwoo Choi
2019-12-17  0:07                     ` Chanwoo Choi
2019-12-17  9:10                     ` Lukasz Luba
2019-12-17  9:10                       ` Lukasz Luba
2020-01-15 15:56                   ` Bartlomiej Zolnierkiewicz
2020-01-15 15:56                     ` Bartlomiej Zolnierkiewicz

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=20191113091336.5218-8-k.konieczny@samsung.com \
    --to=k.konieczny@samsung.com \
    --cc=b.zolnierkie@samsung.com \
    --cc=cw00.choi@samsung.com \
    --cc=k.konieczny@partner.samsung.com \
    --cc=kgene@kernel.org \
    --cc=krzk@kernel.org \
    --cc=kyungmin.park@samsung.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=m.szyprowski@samsung.com \
    --cc=myungjoo.ham@samsung.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.