fpsd_main.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 <string.h>
#include <dirent.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <door.h>
#include <errno.h>
#include <fcntl.h>
#include <strings.h>
#include <unistd.h>
#include <synch.h>
#include <syslog.h>
#include <pthread.h>
#include <thread.h>
#include <signal.h>
#include <limits.h>
#include <locale.h>
#include <sys/systeminfo.h>
#include <sys/processor.h>
#include <ctype.h>
#include <poll.h>
#include <dirent.h>
#include <kstat.h>
#include <libscf.h>
#include <libgen.h>
#include <priv_utils.h>
#include <fpsapi.h>
#include "fpsd.h"
#include "messages.h"
/* Only messages of priority 'debug_level' and lower will be logged */
int debug_level = DFLT_DBG_LVL;
void terminate_process();
/* Local Static Variables */
static int door_id = -1;
static char *str_fps_fmri = NULL;
static int max_cpuids = 0;
/* Local static functions */
static int read_conf_props();
static void fpsd_fini();
static int reprobe_and_reread_config();
static int fpsd_probe_config();
/* ARGSUSED */
void
{
}
void
{
int ret;
if (NO_CPUS_2_TEST == ret) {
while (NO_CPUS_2_TEST == ret) {
sleep(600);
}
}
}
static int
{
int ret;
(void) fpsd_message(FPSD_EXIT_ERROR,
}
ret = fpsd_probe_config();
if (ZERO_INTERVAL == ret) {
}
return (ret);
}
static int
{
int door_fd;
if (door_fd < 0)
return (NO_DAEMON);
return (NO_DAEMON);
}
return (NO_DAEMON);
}
/* Daemon exists; different process */
return (DAEMON_EXISTS);
} else {
return (DAEMON_EXISTS_AND_SAME_PROC); /* Same process */
}
}
static int
fps_setup_door(void)
{
int newfd;
/* Create the door */
if (door_id < 0) {
return (-1);
}
return (-1);
}
}
return (-1);
}
}
return (0);
}
void
{
fpsd_fini();
if (door_id >= 0) {
(void) door_revoke(door_id);
(void) unlink(FPS_DOOR_FILE);
}
}
static int
{
int pfds[2];
int status;
/*
* Block all signals prior to the fork and leave them blocked in
* the parent so we don't get in a situation where the parent gets
* SIGINT and returns non-zero exit status and the child is
* actually running. In the child, restore the signal mask once
* we've done our setsid().
*/
(void) sigfillset(&set);
/*
* If we're the parent process, wait for either the child to send
* us the appropriate exit status over the pipe or for the read to
* fail (presumably with 0 for EOF if our child terminated
* abnormally). If the read fails, exit with either the child's
* exit status if it exited or with FPSD_EXIT_ERROR if it died
* from a fatal signal.
*/
if (pid != 0) { /* Parent */
}
(void) setsid();
(void) chdir("/");
(void) umask(022);
return (pfds[1]);
}
static void
become_daemon_fini(int fd)
{
}
}
/*
* Calculates the number of iterations needed for each testable cpu
* based on the frequency and using the following table. This table
* tells how much time it takes for the matrix sizes on a processor
* with frequencies upto 1000MHz/1500 MHz/ 2000 MHz. This data is
* based on profiling done earlier.
*
* f\p\t| 100 200 300 400 500 600 700 800 900 ms
* ======================================================================
* 1000 1-28 29-50 51-62 63-72 73-81 82-90 91-98 99-105 106-112
* 1500 1-36 37-64 65-80 81-93 94-106 107-115 116-126 127-134 135-144
* 2000 1-39 40-70 71-87 88-102 103-113 114-126 127-137 138-148 149-157
*
* If asc is 0, these iterations will be executed in the descending of
* of matrix size; else the iterations will be executed in the increasing
* order of matrix sizes. This is done to average out the execution time
* as large matrices mean more time to complete the test.
*/
static void
{
const int num_iterations_1K = 112;
const int num_iterations_1500 = 144;
const int num_iterations_2K = 157;
int total_iterations = 0;
int asc = 1;
int i;
int freq;
if (m_stat->m_cpuids_size <= 0) {
}
m_stat->m_num_cpus_to_test = 0;
for (i = 0; i < m_stat->m_cpuids_size; i++) {
continue;
if (freq < 1500) {
} else if (freq < 2000) {
} else {
}
if (asc) {
asc = 0;
} else {
asc = 1;
}
}
}
/*
* Calculates the time interval between the tests invocation in seconds.
* The goal is to complete once all iterations for all cpus in a 24hr
* period.
*/
static int
{
int intvl;
if (total_iterations <= 0) {
}
return (1);
}
/*
* Checks if a platform is supported by looking for the corresponding
* CPU_BRAND = UltraSPARC-III;
*/
static int
{
return (1);
else
return (0);
}
/*
* fpsd_probe(): probes system configuration and
* sets up the fpsd_t structure.
* Returns 0 on success, non-zero on failure.
*
*/
static int
{
int cpu_freq;
int supported;
int i;
int cpuid_index;
/* probe the system and fill in mach_conf_t elements */
/* Reprobe request */
fpsd.d_iteration = 0;
fpsd.d_interval = 0;
fpsd.d_fpuid_index = 0;
m_stat->m_num_fpus = 0;
m_stat->m_num_on_fpuids = 0;
m_stat->m_cpuids_size = 0;
m_stat->total_iter = 0;
m_stat->m_num_cpus_to_test = 0;
}
}
/*
* Find number of online FPUs, and initialize
* m_stat->m_num_on_fpuids. Then collect kstat
* cpu_info for each.
*/
sizeof (processorid_t));
if (NULL == cpuid_list) {
return (-1);
}
cpuid_index = 0;
for (i = 0; i < max_cpuids; i++) {
cpuid_list[cpuid_index++] = i;
}
/* Break after all onln cpuids found */
break;
}
}
/*
* Get cpu-brand info all valid cpuids using kstat.
* This is needed to take care
* of mixed cpu scenario
*/
kstat_ctl = kstat_open();
return (-1);
}
for (i = 0; i < m_stat->m_num_on_fpuids; i++) {
supported = 0;
cpuid_list[i], NULL);
LIBRARY_CALL_FAIL, "kstat_lookup",
return (-1);
}
if (ret != -1) {
"brand");
if (NULL != kstat_cpu_name) {
}
} else {
(void) kstat_close(kstat_ctl);
return (-1);
}
if (!supported) {
cpuid_list[i]);
} else {
}
/* Get frequency */
"clock_MHz");
if (NULL != kstat_cpu_freq) {
} else {
FREQ_PROBE_FAIL, cpuid_list[i]);
return (-1);
}
cpu_freq);
}
if (m_stat->m_num_cpus_to_test <= 0) {
return (-1);
}
return (0);
}
/*
* returns 1 if cpuid is found in the list of cpus to be
* excluded from testing.
*/
static int
ignore_cpu(int cpuid)
{
int found = 0;
int i;
if (ignore_cpus[i] == cpuid) {
found = 1;
}
}
return (found);
}
/*
* This function parses the string of cpu-ids separated by
* "," , constructs the list and disables testing on those
* cpus. This function assumes fpsd_probe has been called and all
* the machine config info is available in structure fpsd.
*/
static int
parse_and_set_cpu_id_list(char *strCPUs)
{
char *last;
int *tmp_cpus;
int num_cpus_to_test = 0;
int i;
int t_cpuid;
char *cpu_id;
static int first_time = 1;
return (-1);
} else {
invalid = 1;
}
/* More than max configurable cpus */
invalid = 1;
}
}
if (num_cpus) {
sizeof (processorid_t) * (int) num_cpus);
for (i = 0; i < num_cpus; i++) {
}
} else {
fpsd.num_ignore_cpus = 0;
}
fpsd.num_ignore_cpus = 0;
}
if (ignore_cpu(t_cpuid)) {
} else {
}
}
if (num_cpus_to_test <= 0) {
if (1 == first_time) {
first_time = 0;
} else {
}
return (NO_CPUS_2_TEST);
}
}
first_time = 1;
return (0);
}
#define CLEAN_UP_SCF_STUFF { \
if (scf_handle_p) { \
} \
if (inst) \
if (pg) \
scf_pg_destroy(pg); \
if (scf_prop_p) \
if (value) \
}
/* Read properties from SMF configuration repository using libscf APIs */
static int
{
int ret_val = -1;
int val;
int name_len;
char *strCPUs;
(NULL == scf_prop_p)) {
scf_strerror(scf_error()));
return (-1);
}
if (val != 0) {
scf_strerror(scf_error()));
return (-1);
}
if (val != 0) {
scf_strerror(scf_error()));
return (-1);
}
/* Read interval property if defined */
if (val == 0) {
if (intvl != 0) {
ret_val = 0;
}
} else {
scf_strerror(scf_error()));
}
/*
* Read property "exclude_cpus" if defined - this is
* the array of cpu-ids to be excluded from testing.
*/
if (val == 0) {
name_len =
LIBRARY_CALL_FAIL, "malloc");
return (-1);
}
ret_val =
strCPUs);
}
} else {
scf_strerror(scf_error()));
}
}
}
/* Clean up */
return (ret_val);
}
static int fpsd_init() {
fpsd.d_iteration = 0;
fpsd.d_interval = 0;
fpsd.d_fpuid_index = 0;
m_conf_p->m_num_fpus = 0;
m_conf_p->m_num_on_fpuids = 0;
m_conf_p->m_cpuids_size = 0;
m_conf_p->total_iter = 0;
m_conf_p->m_num_cpus_to_test = 0;
/*
* Allocate enough memory to accomodate maximum number of CPUs
* supported by this platform.
*/
(int)sysconf(_SC_NPROCESSORS_MAX));
return (1);
else
return (0);
}
static void
fpsd_fini() {
if (fpsd.d_ignore_cpuid)
}
static int
{
int smf_invoked = 0;
int ret = 0;
/*
* Use smf_get_state to get the status of the service to see
* if the status is "online" by now. If so, read the proper-
* ties defined using SCF.
*/
if (NULL != str_fps_fmri) {
strlen(SCF_STATE_STRING_ONLINE)) == 0)) {
smf_invoked = 1;
/* Read SMF properties if invoked thro' SMF */
ret = read_conf_props();
if (ret == NO_CPUS_2_TEST) {
return (ret);
}
} else {
CL_INVOKED, (smf_state) ?
smf_state : "No SMF service named fpsd");
}
}
ret = calculateTimeInterval();
return (ZERO_INTERVAL);
}
}
return (0);
}
int
{
int sig;
/* Pipe fd to write the status back to parent after becoming daemon */
int pfd = -1;
int status = FPSD_INIT_SUCCESS;
char rcvsigstr[32];
int c;
int ret;
char path[MAXPATHLEN];
int probe_status = -1;
const char *progname;
progname++;
else
#ifndef TEXT_DOMAIN /* Should be defined via Makefile */
#define TEXT_DOMAIN "SUNW_FPS"
#endif
(void) textdomain(TEXT_DOMAIN);
if (fpsd_init()) {
}
/*
* Set our per-process core file path to leave core files in
* debugging, and make sure that there is no restriction on core
* file size.
*/
}
}
if (ret == 0) {
}
/* parse arguments */
switch (c) {
case 'd':
break;
case 'l':
if (debug_level < 0)
break;
default:
progname);
break;
}
}
/*
* Reset all of our privilege sets to the minimum set of required
* privileges. We continue to run as root so that files we create
* such as logs and checkpoints are secured in the /var
* filesystem.
*/
0, 0, /* run as uid 0 and gid 0 */
PRIV_NET_PRIVADDR, NULL) != 0) {
}
pfd = become_daemon_init();
} else {
}
if (daemon_exists()) {
/*
* If another instance of fpsd daemon is already running;
* exit. Should not clean up door file
*/
}
/*
* Setup door prevents any more instances of fpsd from running.
*/
if (fps_setup_door() == -1) {
}
/*
* Some desktop platforms satisfy E* guidelines. Here CPU power
* management is enabled by default. The scheduling algorithms
* change on these platforms to not to do testing on idle system
* to save power.
*/
init_estar_db(); /* Initialize Estar config data base */
/* Print message on CPU E* enabled system */
if (is_estar_system)
}
/*
* Now that we're running, if a pipe fd was specified, write an
* exit status to it to indicate that our parent process can
* safely detach.
*/
if (pfd >= 0) {
}
} else {
/*
* Mask all signals before creating sched thread. We will
* unmask selective siganls from main thread. This ensures
* that only main thread handles signals. This is done in
* become_daemon() if we had to daemonize.
*/
(void) sigfillset(&sigs);
}
/*
* Give some time for SMF to read the exit status
* of parent and update fpsd fmri state
*/
if (NULL != str_fps_fmri) {
} else {
}
if (probe_status != 0) {
/* Exit child proces too */
if (NULL != str_fps_fmri) {
}
}
}
/*
* On estar-systems, if interval < MIN_INTERVAL, scheduling tests will
* reset the idle counter and prevent system from going to sleep.
* To avoid this, setting interval to MIN_INTERVAL.
*/
}
(void) sigfillset(&sigs);
/* Run scheduling thread */
}
/*
* We unmask selective signals here. Besides terminating on
* SIGINT & SIGTERM, we handle SIGHUP that is used to cause
* daemon to re-read the SMF properties.
*/
(void) sigemptyset(&sigs);
for (;;) {
if (sig != -1) {
switch (sig) {
case SIGINT:
case SIGTERM:
break;
case SIGHUP:
break;
default: break;
}
}
}
/* NOTREACHED */
return (0);
}