Skip to content

Commit

Permalink
os/pm: Update pm_procfs to show domain information
Browse files Browse the repository at this point in the history
This commit extend pm_procfs to list all the domains inside 'proc/power/domains' directory based on domain_id. Now, every directory in proc/power has 'info' file which shows all the power management information related to current directory.
  • Loading branch information
gSahitya-samsung authored and sunghan-chang committed Sep 5, 2024
1 parent b88dcea commit 55bbf33
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 102 deletions.
3 changes: 2 additions & 1 deletion os/fs/procfs/fs_procfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ static const struct procfs_entry_s g_procfsentries[] = {
#endif

#if defined(CONFIG_PM) && !defined(CONFIG_FS_PROCFS_EXCLUDE_POWER)
{"power/domains**", &power_procfsoperations},
{"power**", &power_procfsoperations},
{"power/*", &power_procfsoperations},
#endif

#if !defined(CONFIG_FS_PROCFS_EXCLUDE_UPTIME)
Expand Down
1 change: 1 addition & 0 deletions os/include/tinyara/pm/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ enum pm_state_e {
*/
PM_COUNT,
};
static const char *pm_state_name[PM_COUNT] = {"PM_NORMAL", "PM_IDLE", "PM_STANDBY", "PM_SLEEP"};

/* This enumeration provides all power management related wakeup source code. */

Expand Down
3 changes: 3 additions & 0 deletions os/pm/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ struct pm_global_s {
/* Timer to decrease state */

WDOG_ID wdog;

/* No. of Registered Domains */
uint16_t ndomains;
};

/****************************************************************************
Expand Down
1 change: 1 addition & 0 deletions os/pm/pm_domain_register.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ int pm_domain_register(char *domain)
return ERROR;
}
strncpy(pm_domain_map[index], domain, length + 1);
g_pmglobals.ndomains++;
#ifdef CONFIG_PM_METRICS
/* For newly registered domain initialize its pm metrics*/
pm_metrics_update_domain(index);
Expand Down
291 changes: 190 additions & 101 deletions os/pm/pm_procfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
struct power_dir_s {
struct procfs_dir_priv_s base; /* Base directory private data */
uint8_t direntry;
int domain_id;
uint8_t dtype;
};

/* This structure describes one open "file" */
Expand Down Expand Up @@ -129,6 +131,22 @@ static int power_stat(const char *relpath, FAR struct stat *buf);
* Private Data
****************************************************************************/

#define POWER "power"
#define POWER_DOMAINS "domains"
#define POWER_STATE "state"
#define POWER_INFO "info"

/*
* Level 1 : /proc/power
* Level 2 : /proc/power/domains
* Level 3 : /proc/power/domains/{domain_name}
* Level 4 : /proc/power/domains/{domain_name}/info
*/

#define POWER_LEVEL_1 1
#define POWER_LEVEL_2 2
#define POWER_LEVEL_3 3
#define POWER_LEVEL_4 4

/****************************************************************************
* Public Data
Expand Down Expand Up @@ -159,6 +177,30 @@ const struct procfs_operations power_procfsoperations = {
* Private Functions
****************************************************************************/

static void power_read_domain_info(int domain_id, void (*readprint)(const char *, ...))
{
readprint("%-15s : %d\n", "Domain ID", domain_id);
readprint("%-15s : %s\n", "Domain Name", pm_domain_map[domain_id]);
readprint("%-15s : %d\n", "Suspend Count", g_pmglobals.suspend_count[domain_id]);
}

static void power_read_domains(void (*readprint)(const char *, ...))
{
int domain_id;
readprint(" DOMAIN ID | DOMAIN NAME | SUSPEND COUNTS \n");
readprint("-----------|-----------------------------------|----------------\n");
for (domain_id = 0; domain_id < g_pmglobals.ndomains; domain_id++) {
readprint(" %9d | %33s | %14d \n", domain_id, pm_domain_map[domain_id], g_pmglobals.suspend_count[domain_id]);
}
}

static void power_read_state(void (*readprint)(const char *, ...))
{
enum pm_state_e pm_state;
for (pm_state = PM_NORMAL; pm_state < PM_COUNT; pm_state++) {
readprint("%s %s\n", (pm_state == g_pmglobals.state) ? "*" : " ", pm_state_name[pm_state]);
}
}
/****************************************************************************
* Name: power_find_dirref
*
Expand All @@ -170,67 +212,76 @@ const struct procfs_operations power_procfsoperations = {

static int power_find_dirref(FAR const char *relpath, FAR struct power_dir_s *dir)
{
uint16_t len;
FAR char *str;
int i;

/* Skip the "power/domains" portion of relpath. We accept it only now */
len = strlen(POWER_SUBDIRPATH_DOMAINS);
if (strncmp(relpath, POWER_SUBDIRPATH_DOMAINS, len) != 0) {
fdbg("Invalid Path : Failed to find path %s \n", relpath);
return -ENOENT;
}
relpath += len;

if (relpath[0] == '/') {
relpath++;
}

if (relpath[0] == '\0') {
/* Requesting directory listing of registered device drivers for PM */
dir->base.level = 1;
dir->base.index = 0;
dir->base.nentries = CONFIG_PM_NDOMAINS;
/* Search PM registry to the number of registered device driver */
return OK;
}

str = NULL;

if (!str) {
fdbg("ERROR: Invalid path \"%s\"\n", relpath);
return -ENOENT;
}

len = str - relpath;

relpath += len;

if (relpath[0] == '/') {
relpath++;
}

if (relpath[0] == '\0') {
/* Requesting directory listing of a specific device driver or entry */
dir->base.level = 2;
dir->base.index = 0;
dir->base.nentries = g_power_direntrycount;
dir->direntry = 0;
return OK;
char *str;
int domain_id;
/* Function to check path is starting with given name */
int checkStart(char *name, bool isDirectory) {
uint16_t length = strlen(name);
if (strncmp(str, name, length) != 0) {
return ERROR;
}
str += length;
if (str[0] == '\0') {
return OK;
}
if (isDirectory && (str[0] == '/')) {
str++;
return OK;
}
return ERROR;
}

/* This is 3 level. Only files exist only in this level. */
dir->base.level = 3;
dir->base.nentries = 1;
dir->base.index = 0;
dir->direntry = 0;
for (i = 0; i < g_power_direntrycount; i++) {
if (strcmp(relpath, g_power_direntry[i].name) == 0) {
dir->direntry = i;
str = relpath;
dir->domain_id = -1;
/* Check relpath has "power" mount point */
if (checkStart(POWER, true) == OK) {
if (str[0] == '\0') {
dir->base.level = POWER_LEVEL_1;
dir->base.index = 0;
dir->base.nentries = 2;
dir->dtype = DTYPE_DIRECTORY;
return OK;
}
/* Check relpath has "domains" mount point */
if (checkStart(POWER_DOMAINS, true) == OK) {
if (str[0] == '\0') {
dir->base.level = POWER_LEVEL_2;
dir->base.index = 0;
dir->base.nentries = g_pmglobals.ndomains + 1;
dir->dtype = DTYPE_DIRECTORY;
return OK;
}
/* Iterate over each domain_name till you find match */
for (domain_id = 0; domain_id < g_pmglobals.ndomains; domain_id++) {
if (checkStart(pm_domain_map[domain_id], true) == OK) {
dir->domain_id = domain_id;
if (str[0] == '\0') {
dir->base.level = POWER_LEVEL_3;
dir->base.index = 0;
dir->base.nentries = 1;
dir->dtype = DTYPE_DIRECTORY;
return OK;
}
break;
}
}
/* Check relpath has "info" mount point */
if (checkStart(POWER_INFO, false) == OK) {
dir->base.level = (dir->domain_id != -1) ? POWER_LEVEL_4 : POWER_LEVEL_3;
dir->base.index = 0;
dir->base.nentries = 0;
dir->dtype = DTYPE_FILE;
return OK;
}
/* Check relpath has "state" mount point */
} else if (checkStart(POWER_STATE, false) == OK) {
dir->base.level = POWER_LEVEL_2;
dir->base.index = 0;
dir->base.nentries = 0;
dir->dtype = DTYPE_FILE;
return OK;
}
}

fdbg("Invalid Path : Failed to find path %s \n", relpath);
return -ENOENT;
}

Expand Down Expand Up @@ -296,29 +347,56 @@ static int power_close(FAR struct file *filep)
static ssize_t power_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
{
FAR struct power_file_s *priv;
ssize_t ret;

fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);

/* Recover our private data from the struct file instance */

priv = (FAR struct power_file_s *)filep->f_priv;
DEBUGASSERT(priv);

/* Perform the read based on the directory entry */

ret = 0;

if (priv->dir.base.level == 3 && priv->dir.direntry < g_power_direntrycount) {
ret = g_power_direntry[priv->dir.direntry].read(filep, buffer, buflen);
size_t totalsize;
int domain_id;
int last_read;
int pm_state;
/* Function to copy domain information into buffer */
void readprint(const char *format, ...) {
size_t copysize;
va_list args;
va_start(args, format);
copysize = vsnprintf(buffer, buflen, format, args);
va_end(args);
/* Take min of buflen and copysize */
if (buflen < copysize) {
copysize = buflen - 1;
}
/* No need to copy information if we have already read information earlier */
if (copysize <= last_read) {
last_read -= copysize;
return;
}
/* Overwrite the last read information */
if (last_read) {
copysize -= last_read;
strncpy(buffer, buffer + last_read, copysize);
last_read = 0;
}
/* Increment the buffer pointer */
buflen -= copysize;
buffer += copysize;
totalsize += copysize;
priv->offset += copysize;
}

/* Update the file offset */
if (ret > 0) {
filep->f_pos += ret;
priv = (FAR struct power_file_s *)filep->f_priv;
domain_id = priv->dir.domain_id;
totalsize = 0;
last_read = priv->offset;
/* Read the content of "{domain_name}/info" */
if ((priv->dir.base.level == POWER_LEVEL_4) && (priv->dir.base.index == 0) && (domain_id >= 0)) {
power_read_domain_info(domain_id, readprint);
/* Read the content of "domains/info" */
} else if ((priv->dir.base.level == POWER_LEVEL_3) && (priv->dir.base.index == 0)) {
power_read_domains(readprint);
/* Read the content of "power/state" */
} else if ((priv->dir.base.level == POWER_LEVEL_2) && (priv->dir.base.index == 0)) {
power_read_state(readprint);
}

return ret;
/* Indicate we have already provided all the data */
filep->f_pos += totalsize;
return totalsize;
}

/****************************************************************************
Expand All @@ -337,7 +415,7 @@ static ssize_t power_write(FAR struct file *filep, FAR const char *buffer, size_
priv = (FAR struct power_file_s *)filep->f_priv;
DEBUGASSERT(priv);

/* Perform the read based on the directory entry */
/* pm_procfs does not have write operation, one must code write logic here */

ret = 0;

Expand Down Expand Up @@ -466,35 +544,46 @@ static int power_readdir(struct fs_dirent_s *dir)
/* Have we reached the end of the directory */
index = powerdir->base.index;
if (index >= powerdir->base.nentries) {
fdbg("Entry %d: End of directory\n", index);
return -ENOENT;
}

if (powerdir->base.level < 1 || powerdir->base.level > 3) {
fdbg("Invalid directory level\n");
return -ENOENT;
}

if (powerdir->base.level == 1) {
/* Listing the contents of domains */
if (index >= 0 && index < CONFIG_PM_NDOMAINS) {
switch (powerdir->base.level) {
/* List the content of "power" directory */
case POWER_LEVEL_1:
switch (index) {
case 0:
dir->fd_dir.d_type = DTYPE_DIRECTORY;
snprintf(dir->fd_dir.d_name, sizeof(dir->fd_dir.d_name), "%d", index);
snprintf(dir->fd_dir.d_name, sizeof(dir->fd_dir.d_name), POWER_DOMAINS);
powerdir->base.index++;
return OK;
break;
case 1:
dir->fd_dir.d_type = DTYPE_FILE;
snprintf(dir->fd_dir.d_name, sizeof(dir->fd_dir.d_name), POWER_STATE);
powerdir->base.index++;
break;
}
break;
/* List the content of "domains" directory */
case POWER_LEVEL_2:
if (index == g_pmglobals.ndomains) {
dir->fd_dir.d_type = DTYPE_FILE;
snprintf(dir->fd_dir.d_name, sizeof(dir->fd_dir.d_name), POWER_INFO);
} else {
dir->fd_dir.d_type = DTYPE_DIRECTORY;
snprintf(dir->fd_dir.d_name, sizeof(dir->fd_dir.d_name), pm_domain_map[index]);
}
} else if (powerdir->base.level == 2) {
/* Listing the contents of a specific domain */
dir->fd_dir.d_type = g_power_direntry[powerdir->base.index].type;
strncpy(dir->fd_dir.d_name, g_power_direntry[powerdir->base.index].name, NAME_MAX + 1);
powerdir->base.index++;
return OK;
} else if (powerdir->base.level == 3) {
fdbg("%s : Not a directory\n", g_power_direntry[index].name);
return -ENOTDIR;
break;
/* List the content of "{domain_name}" directory*/
case POWER_LEVEL_3:
dir->fd_dir.d_type = DTYPE_FILE;
snprintf(dir->fd_dir.d_name, sizeof(dir->fd_dir.d_name), POWER_INFO);
powerdir->base.index++;
break;
default:
fdbg("Invalid directory level\n");
return -ENOENT;
}

return -ENOENT;
return OK;
}

/****************************************************************************
Expand Down Expand Up @@ -534,7 +623,7 @@ static int power_stat(const char *relpath, struct stat *buf)
ret = power_find_dirref(relpath, &dir);

if (ret == OK) {
if (dir.base.level < 3) {
if (dir.dtype == DTYPE_DIRECTORY) {
/* This is a directory */
buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
} else {
Expand Down

0 comments on commit 55bbf33

Please sign in to comment.