[dpdk-dev] [PATCH 2/3] power: switching to unbuffered access for /sys files

Hunt, David david.hunt at intel.com
Thu Nov 23 15:42:07 CET 2017


Hi Radoslaw,


On 11/11/2017 6:55 PM, Radoslaw Biernacki wrote:
> This patch fixes the bug caused by improper use of buffered stdio file
> access for switching the CPU frequency and governor. When using
> buffered stdio, each fwrite() must use fflush() and the return code
> must be verified. Also fseek() is needed.  Therefore it is better to
> use unbuffered mode or use plain open()/write() functions.  This fix
> use second approach. This not only remove need for ffush() but also
> remove need for fseek() operations.  This patch also reuse some code
> around power_set_governor_userspace() and
> power_set_governor_userspace() functions.
>
> Fixes: 445c6528b55f ("power: common interface for guest and host")
> CC: stable at dpdk.org
>
> Signed-off-by: Radoslaw Biernacki <radoslaw.biernacki at linaro.org>
> ---
>   lib/librte_power/rte_power_acpi_cpufreq.c | 211 +++++++++++++-----------------
>   1 file changed, 91 insertions(+), 120 deletions(-)
>
> diff --git a/lib/librte_power/rte_power_acpi_cpufreq.c b/lib/librte_power/rte_power_acpi_cpufreq.c
> index 3d0872f..f811bd3 100644
> --- a/lib/librte_power/rte_power_acpi_cpufreq.c
> +++ b/lib/librte_power/rte_power_acpi_cpufreq.c
> @@ -30,7 +30,7 @@
>    *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
>    *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>    */
> -
> +#include <assert.h>
>   #include <stdio.h>
>   #include <sys/types.h>
>   #include <sys/stat.h>
> @@ -47,6 +47,12 @@
>   #include "rte_power_acpi_cpufreq.h"
>   #include "rte_power_common.h"
>   
> +#define min(_x, _y) ({ \
> +	typeof(_x) _min1 = (_x); \
> +	typeof(_y) _min2 = (_y); \
> +	(void) (&_min1 == &_min2); \
> +	_min1 < _min2 ? _min1 : _min2; })
> +
>   #ifdef RTE_LIBRTE_POWER_DEBUG
>   #define POWER_DEBUG_TRACE(fmt, args...) do { \
>   		RTE_LOG(ERR, POWER, "%s: " fmt, __func__, ## args); \
> @@ -88,7 +94,7 @@ struct rte_power_info {
>   	unsigned lcore_id;                   /**< Logical core id */
>   	uint32_t freqs[RTE_MAX_LCORE_FREQS]; /**< Frequency array */
>   	uint32_t nb_freqs;                   /**< number of available freqs */
> -	FILE *f;                             /**< FD of scaling_setspeed */
> +	int fd;                              /**< FD of scaling_setspeed */
>   	char governor_ori[32];               /**< Original governor name */
>   	uint32_t curr_idx;                   /**< Freq index in freqs array */
>   	volatile uint32_t state;             /**< Power in use state */
> @@ -105,6 +111,9 @@ static struct rte_power_info lcore_power_info[RTE_MAX_LCORE];
>   static int
>   set_freq_internal(struct rte_power_info *pi, uint32_t idx)
>   {
> +	char buf[BUFSIZ];
> +	int count, ret;
> +
>   	if (idx >= RTE_MAX_LCORE_FREQS || idx >= pi->nb_freqs) {
>   		RTE_LOG(ERR, POWER, "Invalid frequency index %u, which "
>   				"should be less than %u\n", idx, pi->nb_freqs);
> @@ -117,17 +126,14 @@ set_freq_internal(struct rte_power_info *pi, uint32_t idx)
>   
>   	POWER_DEBUG_TRACE("Freqency[%u] %u to be set for lcore %u\n",
>   			idx, pi->freqs[idx], pi->lcore_id);
> -	if (fseek(pi->f, 0, SEEK_SET) < 0) {
> -		RTE_LOG(ERR, POWER, "Fail to set file position indicator to 0 "
> -				"for setting frequency for lcore %u\n", pi->lcore_id);
> +	count = snprintf(buf, sizeof(buf), "%u", pi->freqs[idx]);
> +	assert((size_t)count < sizeof(buf)-1);
> +	ret = write(pi->fd, buf, count);
> +	if (ret != count) {
> +		RTE_LOG(ERR, POWER, "Fail to write new frequency (%s) for "
> +				"lcore %u\n", buf, pi->lcore_id);
>   		return -1;
>   	}
> -	if (fprintf(pi->f, "%u", pi->freqs[idx]) < 0) {
> -		RTE_LOG(ERR, POWER, "Fail to write new frequency for "
> -				"lcore %u\n", pi->lcore_id);
> -		return -1;
> -	}
> -	fflush(pi->f);
>   	pi->curr_idx = idx;
>   
>   	return 1;
> @@ -139,90 +145,109 @@ set_freq_internal(struct rte_power_info *pi, uint32_t idx)
>    * governor will be saved for rolling back.
>    */
>   static int
> -power_set_governor_userspace(struct rte_power_info *pi)
> +power_set_governor(unsigned int lcore_id, const char *new_gov, char *old_gov,
> +		   size_t old_gov_len)
>   {
> -	FILE *f;
> +	int fd;
> +	int count, len;
>   	int ret = -1;
>   	char buf[BUFSIZ];
>   	char fullpath[PATH_MAX];
> -	char *s;
> -	int val;
>   
>   	snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_GOVERNOR,
> -			pi->lcore_id);
> -	f = fopen(fullpath, "rw+");
> -	if (!f) {
> +		 lcore_id);
> +	fd = open(fullpath, O_RDWR);
> +	if (fd < 0) {
>   		RTE_LOG(ERR, POWER, "Failed to open %s\n", fullpath);
>   		return ret;
>   	}
>   
> -	s = fgets(buf, sizeof(buf), f);
> -	if (!s) {
> -		RTE_LOG(ERR, POWER, "fgets returns nothing\n");
> +	count = read(fd, buf, sizeof(buf));
> +	if (count < 0) {
> +		RTE_LOG(ERR, POWER, "Failed to read from %s\n", fullpath);
>   		goto out;
>   	}
> +	buf[min((size_t)count, sizeof(buf)-1)] = '\0';
>   
> -	/* Check if current governor is userspace */
> -	if (strncmp(buf, POWER_GOVERNOR_USERSPACE,
> -			sizeof(POWER_GOVERNOR_USERSPACE)) == 0) {
> +	/* Check if current governor is as requested */
> +	if (!strcmp(buf, new_gov)) {
>   		ret = 0;
>   		POWER_DEBUG_TRACE("Power management governor of lcore %u is "
> -				"already userspace\n", pi->lcore_id);
> -		goto out;
> -	}
> -	/* Save the original governor */
> -	snprintf(pi->governor_ori, sizeof(pi->governor_ori), "%s", buf);
> -
> -	/* Write 'userspace' to the governor */
> -	val = fseek(f, 0, SEEK_SET);
> -	if (val < 0) {
> -		RTE_LOG(ERR, POWER, "fseek failed\n");
> +				  "already %s\n", lcore_id, new_gov);
>   		goto out;
>   	}
> +	/* Save the old governor */
> +	if (old_gov)
> +		snprintf(old_gov, old_gov_len, "%s", buf);
>   
> -	val = fputs(POWER_GOVERNOR_USERSPACE, f);
> -	if (val < 0) {
> -		RTE_LOG(ERR, POWER, "fputs failed\n");
> +	/* Set new governor */
> +	len = strlen(new_gov);
> +	count = write(fd, new_gov, len);
> +	if (count != len) {
> +		RTE_LOG(ERR, POWER, "Failed to set %s governor\n", new_gov);
>   		goto out;
>   	}
>   
>   	ret = 0;
>   	RTE_LOG(INFO, POWER, "Power management governor of lcore %u has been "
> -			"set to user space successfully\n", pi->lcore_id);
> +		"set to user space successfully\n", lcore_id);
>   out:
> -	fclose(f);
> +	if (close(fd))
> +		RTE_LOG(ERR, POWER, "Error while closing file %s\n", fullpath);
>   
>   	return ret;
>   }
>   
>   /**
> + * It is to check the current scaling governor by reading sys file, and then
> + * set it into 'userspace' if it is not by writing the sys file. The original
> + * governor will be saved for rolling back.
> + */
> +static int
> +power_set_governor_userspace(struct rte_power_info *pi)
> +{
> +	return power_set_governor(pi->lcore_id, POWER_GOVERNOR_USERSPACE,
> +				  pi->governor_ori, sizeof(pi->governor_ori));
> +}
> +
> +/**
> + * It is to check the governor and then set the original governor back if
> + * needed by writing the the sys file.
> + */
> +static int
> +power_set_governor_original(struct rte_power_info *pi)
> +{
> +	return power_set_governor(pi->lcore_id, pi->governor_ori, NULL, 0);
> +}
> +
> +/**
>    * It is to get the available frequencies of the specific lcore by reading the
>    * sys file.
>    */
>   static int
>   power_get_available_freqs(struct rte_power_info *pi)
>   {
> -	FILE *f;
> +	int fd;
>   	int ret = -1, i, count;
>   	char *p;
>   	char buf[BUFSIZ];
>   	char fullpath[PATH_MAX];
>   	char *freqs[RTE_MAX_LCORE_FREQS];
> -	char *s;
>   
>   	snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_AVAIL_FREQ,
> -			pi->lcore_id);
> -	f = fopen(fullpath, "r");
> -	if (!f) {
> +		 pi->lcore_id);
> +	fd = open(fullpath, O_RDONLY);
> +	if (fd < 0) {
>   		RTE_LOG(ERR, POWER, "Failed to open %s\n", fullpath);
>   		return ret;
>   	}
>   
> -	s = fgets(buf, sizeof(buf), f);
> -	if (!s) {
> -		RTE_LOG(ERR, POWER, "fgets returns nothing\n");
> +	count = read(fd, buf, sizeof(buf));
> +	if (count < 0) {
> +		RTE_LOG(ERR, POWER, "Failed to read from %s\n", fullpath);
>   		goto out;
>   	}
> +	buf[min((size_t)count, sizeof(buf)-1)] = '\0';
>   
>   	/* Strip the line break if there is */
>   	p = strchr(buf, '\n');
> @@ -267,7 +292,8 @@ power_get_available_freqs(struct rte_power_info *pi)
>   	POWER_DEBUG_TRACE("%d frequencie(s) of lcore %u are available\n",
>   			count, pi->lcore_id);
>   out:
> -	fclose(f);
> +	if (close(fd))
> +		RTE_LOG(ERR, POWER, "Error while closing file %s\n", fullpath);
>   
>   	return ret;
>   }
> @@ -278,37 +304,39 @@ power_get_available_freqs(struct rte_power_info *pi)
>   static int
>   power_init_for_setting_freq(struct rte_power_info *pi)
>   {
> -	FILE *f;
> +	int fd;
> +	int count;
> +	uint32_t i, freq;
>   	char fullpath[PATH_MAX];
>   	char buf[BUFSIZ];
> -	uint32_t i, freq;
> -	char *s;
>   
>   	snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_SETSPEED,
>   			pi->lcore_id);
> -	f = fopen(fullpath, "rw+");
> -	if (!f) {
> +	fd = open(fullpath, O_RDWR);
> +	if (fd < 0) {
>   		RTE_LOG(ERR, POWER, "Failed to open %s\n", fullpath);
>   		return -1;
>   	}
>   
> -	s = fgets(buf, sizeof(buf), f);
> -	if (!s) {
> -		RTE_LOG(ERR, POWER, "fgets returns nothing\n");
> +	count = read(fd, buf, sizeof(buf));
> +	if (count < 0) {
> +		RTE_LOG(ERR, POWER, "Failed to read from %s\n", fullpath);
>   		goto out;
>   	}
> +	buf[min((size_t)count, sizeof(buf)-1)] = '\0';
>   
>   	freq = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
>   	for (i = 0; i < pi->nb_freqs; i++) {
>   		if (freq == pi->freqs[i]) {
>   			pi->curr_idx = i;
> -			pi->f = f;
> +			pi->fd = fd;
>   			return 0;
>   		}
>   	}
>   
>   out:
> -	fclose(f);
> +	if (close(fd))
> +		RTE_LOG(ERR, POWER, "Error while closing file %s\n", fullpath);
>   
>   	return -1;
>   }
> @@ -373,66 +401,6 @@ rte_power_acpi_cpufreq_init(unsigned lcore_id)
>   	return -1;
>   }
>   
> -/**
> - * It is to check the governor and then set the original governor back if
> - * needed by writing the the sys file.
> - */
> -static int
> -power_set_governor_original(struct rte_power_info *pi)
> -{
> -	FILE *f;
> -	int ret = -1;
> -	char buf[BUFSIZ];
> -	char fullpath[PATH_MAX];
> -	char *s;
> -	int val;
> -
> -	snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_GOVERNOR,
> -			pi->lcore_id);
> -	f = fopen(fullpath, "rw+");
> -	if (!f) {
> -		RTE_LOG(ERR, POWER, "Failed to open %s\n", fullpath);
> -		return ret;
> -	}
> -
> -	s = fgets(buf, sizeof(buf), f);
> -	if (!s) {
> -		RTE_LOG(ERR, POWER, "fgets returns nothing\n");
> -		goto out;
> -	}
> -
> -	/* Check if the governor to be set is the same as current */
> -	if (strncmp(buf, pi->governor_ori, sizeof(pi->governor_ori)) == 0) {
> -		ret = 0;
> -		POWER_DEBUG_TRACE("Power management governor of lcore %u "
> -				"has already been set to %s\n",
> -				pi->lcore_id, pi->governor_ori);
> -		goto out;
> -	}
> -
> -	/* Write back the original governor */
> -	val = fseek(f, 0, SEEK_SET);
> -	if (val < 0) {
> -		RTE_LOG(ERR, POWER, "fseek failed\n");
> -		goto out;
> -	}
> -
> -	val = fputs(pi->governor_ori, f);
> -	if (val < 0) {
> -		RTE_LOG(ERR, POWER, "fputs failed\n");
> -		goto out;
> -	}
> -
> -	ret = 0;
> -	RTE_LOG(INFO, POWER, "Power management governor of lcore %u "
> -			"has been set back to %s successfully\n",
> -			pi->lcore_id, pi->governor_ori);
> -out:
> -	fclose(f);
> -
> -	return ret;
> -}
> -
>   int
>   rte_power_acpi_cpufreq_exit(unsigned lcore_id)
>   {
> @@ -452,8 +420,11 @@ rte_power_acpi_cpufreq_exit(unsigned lcore_id)
>   	}
>   
>   	/* Close FD of setting freq */
> -	fclose(pi->f);
> -	pi->f = NULL;
> +	if (close(pi->fd)) {
> +		RTE_LOG(ERR, POWER, "Error while closing governor file\n");
> +		return -1;
> +	}
> +	pi->fd = -1;
>   
>   	/* Set the governor back to the original */
>   	if (power_set_governor_original(pi) < 0) {

Could you re-base on top of 17.11? It fails to apply currently.

I think there is a little much going on in this patch, there are a 
couple of discreet changes that could/should be split into individual 
patches.
Your first patch in this series does this well, remove the macros.
I think this one could be split into two patches:
     1. First the switch to unbuffered file access, including the extra 
error checking.
     2. Then rework the functions, i.e. add the new "power_set_governor" 
function and have "_userspace" and "_original" call that.
Do you agree?
Regards,
Dave



More information about the dev mailing list