fpsd_esutil.c revision 60c45ed01d4f99571d468c42f609d11a099fab1e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <pthread.h>
#include <thread.h>
#include <time.h>
#include <sys/systeminfo.h>
#include <dirent.h>
#include <libdevinfo.h>
#include <locale.h>
#include "fpsapi.h"
#include "fpsd.h"
#include "messages.h"
#define DEFAULT_CPU_FULL_POWER 3
int is_estar_system = 0; /* Not an E* system, by default */
static int num_cpus = 0; /* Used only on E* system */
static int full_pwr = DEFAULT_CPU_FULL_POWER;
/*
* enable system default info logging accordingly.
* Note: Even for systems for which CPU PM is not enabled by
* default, disk PM may be enabled explicitly using power.conf;
* If power management is enabled, disable informational logging
* by default.
* The platforms on which CPU PM is enabled by default, would
*
* Note: open_dev_pm() should have been called initially before
* calling this function.
*
*/
void
{
int pm_stat;
if (devpm_fd == -1)
return;
if (pm_stat == -1)
return;
}
/*
* Some platforms don't support power management. (neither CPU nor disk)
* and logging is disabled on those platforms.)
* Some platforms support PM for both disks and CPUs (apart from others).
* Note that even desktops which support CPU PM E* can be custom
* configured to remove power management drivers. In that case,
*
*/
static void open_dev_pm()
{
}
/*
* Initialize Estar info database.
*
*/
void
{
int cpu_i;
int is_pmprop_found = 0;
/*
* Note that this needs to be open on all power management supported
* systems. Some systems support power mgmt on only some
* some platforms. Also PM drivers can be removed on custom
* configurations.
*/
open_dev_pm();
if (devpm_fd == -1)
return;
if (DI_NODE_NIL == fps_di_root) {
}
fps_di_prom = di_prom_init();
if (DI_PROM_HANDLE_NIL == fps_di_prom) {
}
goto exit_es;
/*
* As a final check, also check for "us" driver property pm-components
* On Estar systems, the driver should define this property.
*/
if (DI_NODE_NIL == node) {
goto exit_es;
}
is_pmprop_found = 0;
is_pmprop_found = 1;
break;
}
}
if (!is_pmprop_found)
goto exit_es;
num_cpus = 0;
while (node != DI_NODE_NIL) {
num_cpus++;
}
cpu_i = 0;
continue;
/*
* Keep the mapping between path and processor IDs.
* Currently, processor IDs are not used. But may be used in future.
*/
/*
* On workstation platforms (where CPU E* supported),
* processor ID and instance numbers are same.
* This may change in future. So watch out.
*/
cpu_i++;
}
/* Initialize what "FULL POWER" mode is. */
if (full_pwr == -1)
if (fps_di_root != DI_NODE_NIL) {
}
if (DI_PROM_HANDLE_NIL != fps_di_prom) {
}
}
/*
* Return the min(idle_times), min(remaining_times), max(rem_time) for all
* CPUs in full power mode. The "remain time" is the remaining
* threshold time after which the CPU will make next lower level
* power transition if left idle.
* If the CPUs are not in full power mode or could not exactly determine
* the power mode then return -1.
* return 0 if CPUs are in full power mode.
*/
int
{
int idle_time;
int pmstats[2];
int i;
int ret;
*min_idle = -1;
*min_rem = -1;
*max_rem = -1;
for (i = 0; i < num_cpus; i++) {
if (idle_time == -1)
continue;
/* Now pmstats[0] = cur power level; pmstats[1]=remain time */
if (ret == -1)
continue;
continue;
/*
* The remain time can be negative if there are 2 cpus
* and 1 cpu is ready to transition and the other one is not
*/
if (*min_rem < 0)
*min_rem = 0;
}
}
return
}
/*
* Wait until CPU comes to full power state or timeout occurs.
* If multiple threads call this function, execute the
* PM ioctl system call only once.
* This is better than all 3 threads polling cpu pwr state same time.
*
* Callers of this function should not assume that on returning from
* this function CPU will be in full power state.
* (They should check again).
* This function just optimizes for performance during wait.
*
*
*/
void
{
int res;
static int is_active = 0;
static pm_state_change_t pmsc;
static char path[MAXPATHLEN];
int pwr = 0;
int cur_lvl = 0; /* 0 = unknown. 1=low, 3=full power */
(void) mutex_lock(&wrlck);
if (!is_active) { /* This is the first thread trying to wait */
is_active = 1;
(void) mutex_unlock(&wrlck);
path[0] = 0; /* init not required. Just in case... */
/*
* PM starts buffering the state changes after the first call to
*
* The PM_GET_STATE_CHANGE is a non-blocking call where as _WAIT is
* blocking call. The PM_GET_STATE_CHANGE also returns all the info
* about the latest buffered state change if already buffered event is
* available. So it is important to drain out all old events,
* if you are only interested in future events.
*
* After the state changes the exact information/timestamp about
* state changes are reflected in the ioctl struct.
* To keep things simple, after draining out all buffered info,
* we issue get current power to get the current power level and
* then we issue another _WAIT command to get the next power change.
*
*/
do {
/* 1 second sleep. Avoid busy loop */
/* Probably will succeed in next call. */
goto psc_complete;
}
} while (errno != EWOULDBLOCK);
/* drain out all buffered state changes */
/* If current state is full power, then get out. */
do {
if (pwr != -1) break;
continue;
} else {
goto psc_complete;
}
/*CONSTCOND*/
} while (1);
goto psc_complete;
path[0] = 0; /* init not required. Just in case... */
do {
/* 1 second sleep */
}
if (res == -1) {
/*
* If there are failures in state change ioctl, just would
* fall back to normal polling of status later. get out quiet.
*/
/* avoid busy loop -- 1 second sleep */
goto psc_complete;
}
}
(void) mutex_lock(&wrlck);
is_active = 0;
(void) mutex_unlock(&wrlck);
} else {
/* Release the lock first */
(void) mutex_unlock(&wrlck);
/*
* Already one other thread is active issuing ioctl call.
* Just poll here to check the local flag without any expensive
* ioctl calls until the transition is complete.
*/
for (;;) {
(void) mutex_lock(&wrlck);
if (!is_active) {
(void) mutex_unlock(&wrlck);
break;
}
(void) mutex_unlock(&wrlck);
}
}
}