/*
* 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"
/*
* The plgrp utility allows a user to display and modify the home lgroup and
* lgroup affinities of the specified threads
*/
#include <ctype.h>
#include <errno.h>
#include <libintl.h>
#include <libproc.h>
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/lgrp_user.h>
/*
* Delimiters
*/
/*
* Exit values other than EXIT_{SUCCESS,FAILURE}
*/
/*
* Header and format strings
*/
/*
* Part of the HDR_PLGRP_AFF_SET header used to calculate space needed to
* represent changing home as old => new
*/
/*
* How much to allocate for lgroup bitmap array as it grows
*/
/*
* Strings that can be given for lgroups
*/
/*
* Strings corresponding to lgroup affinities
*/
/*
* Invalid value for lgroup affinity
*/
/*
* Number of args needed for lgroup system call
*/
#ifndef TEXT_DOMAIN /* should be defined by cc -D */
#endif
/*
* plgrp(1) operations
*/
typedef enum plgrp_ops {
} plgrp_ops_t;
/*
* Arguments specified to plgrp(1) and any state needed to do everything
* that plgrp(1) does for one operation from inside Plwp_iter_all()
*/
typedef struct plgrp_args {
} plgrp_args_t;
/*
* How many signals caught from terminal
* We bail out as soon as possible when interrupt is set
*/
static int interrupt = 0;
/*
* How many non-fatal errors ocurred
*/
static int nerrors = 0;
/*
* Name of this program
*/
static char *progname;
/*
* Root of the lgroup hierarchy
*/
/*
* Bitmap of all lgroups in the system
*/
/*
* Size of lgrps_bitmap array
*/
static int lgrps_bitmap_nelements = 0;
/*
* Macro LGRP_VALID returns true when lgrp is present in the system.
*/
/*
* Maximum lgroup value.
*/
/*
* Total possible number of lgroups
*/
static void
{
gettext("\t%s [-F] -A <lgroup list>/none|weak|strong[,...] "
" <pid>[/lwps] ...\n"), progname);
gettext("\n\twhere <lgroup list> is a comma separated list of\n"
"\tone or more of the following:\n\n"
"\t - lgroup ID\n"
"\t - Range of lgroup IDs specified as\n"
"\t\t<start lgroup ID>-<end lgroup ID>\n"
"\t - \"all\"\n"
"\t - \"root\"\n"
"\t - \"leaves\"\n\n"));
}
/*
* Handler for catching signals from terminal
*/
/* ARGSUSED */
static void
{
interrupt++;
}
/*
* Return string name for given lgroup affinity
*/
static char *
{
switch (aff) {
case LGRP_AFF_STRONG:
rc = "strong";
break;
case LGRP_AFF_WEAK:
rc = "weak";
break;
case LGRP_AFF_NONE:
rc = "none";
break;
default:
break;
}
return (rc);
}
/*
* Add a new lgroup into lgroup array in "arg", growing lgroup and affinity
* arrays if necessary
*/
static void
{
progname);
}
progname);
}
}
}
/*
* Return an array having '1' for each lgroup present in given subtree under
* specified lgroup in lgroup hierarchy
*/
static void
int *bitmap_nelements)
{
int i;
int nchildren;
if (lgrpid < 0) {
if (lgrpid < 0)
return;
}
/*
* If new lgroup cannot fit, grow the array and fill unused portion
* with zeroes.
*/
while (lgrpid >= *bitmap_nelements) {
*bitmap_nelements * sizeof (char));
if (*bitmap_array == NULL) {
progname);
}
(*bitmap_nelements - NLGRPS) * sizeof (char));
}
/*
* Insert lgroup into bitmap and update max lgroup ID seen so far
*/
if (lgrpid > max_lgrpid)
max_lgrpid = lgrpid;
/*
* Get children of specified lgroup and insert descendants of each
* of them
*/
if (nchildren > 0) {
progname);
}
nchildren) {
return;
}
for (i = 0; i < nchildren; i++)
}
}
/*
* Parse lgroup affinity from given string
*
* Return lgroup affinity or LGRP_AFF_INVALID if string doesn't match any
* existing lgroup affinity and return pointer to position just after affinity
* string.
*/
static lgrp_affinity_t
{
return (LGRP_AFF_INVALID);
/*
* Skip delimiter
*/
if (string[0] == DELIMIT_AFF)
string++;
/*
* Return lgroup affinity matching string
*/
== 0) {
rc = LGRP_AFF_NONE;
rc = LGRP_AFF_WEAK;
strlen(LGRP_AFF_STRONG_STR)) == 0) {
}
return (rc);
}
/*
* Parse lgroups from given string
* Returns the set containing all lgroups parsed or NULL.
*/
static int
{
lgrp_id_t i;
char *token;
return (0);
/*
* Parse first lgroup (if any)
*/
return (-1);
do {
/*
* Parse lgroups
*/
char *p;
/*
* lgroup ID(s)
*
* Can be <lgroup ID>[-<lgroup ID>]
*/
if (p == NULL)
else
/*
* Add valid lgroups to lgroup array
*/
if ((i >= 0) && (i < NLGRPS) && LGRP_VALID(i))
lgrps_add_lgrp(arg, i);
else {
gettext("%s: bad lgroup %d\n"),
progname, i);
nerrors++;
}
}
strlen(LGRP_ALL_STR)) == 0) {
/*
* Add "all" lgroups to lgroups array
*/
for (i = 0; i < NLGRPS; i++) {
if (LGRP_VALID(i))
lgrps_add_lgrp(arg, i);
}
strlen(LGRP_ROOT_STR)) == 0) {
if (root < 0)
strlen(LGRP_LEAVES_STR)) == 0) {
/*
* Add leaf lgroups to lgroups array
*/
for (i = 0; i < NLGRPS; i++) {
if (LGRP_VALID(i) &&
lgrps_add_lgrp(arg, i);
}
} else {
return (-1);
}
return (0);
}
/*
* Print array of lgroup IDs, collapsing any consecutive runs of IDs into a
* range (eg. 2,3,4 into 2-4)
*/
static void
{
int i;
/*
* Initial range consists of the first element
*/
for (i = 1; i < nlgrps; i++) {
/*
* Got consecutive lgroup ID, so extend end of range
* without printing anything since the range may extend
* further
*/
} else {
/*
* Next lgroup ID is not consecutive, so print lgroup
* IDs gotten so far.
*/
} else { /* different values */
}
/*
* Try finding consecutive range starting from this
* lgroup ID
*/
}
}
/*
* Print last lgroup ID(s)
*/
} else {
}
}
/*
* Print lgroup affinities given array of lgroups, corresponding array of
* affinities, and number of elements.
* Skip any lgroups set to LGRP_NONE or having invalid affinity.
*/
static void
{
int i;
int nlgrps_none;
int nlgrps_strong;
int nlgrps_weak;
progname);
interrupt = 1;
return;
}
/*
* Group lgroups by affinity
*/
for (i = 0; i < nelements; i++) {
/*
* Skip any lgroups set to LGRP_NONE
*/
continue;
switch (affs[i]) {
case LGRP_AFF_STRONG:
break;
case LGRP_AFF_WEAK:
break;
case LGRP_AFF_NONE:
break;
default:
/*
* Skip any lgroups with invalid affinity.
*/
break;
}
}
/*
* Print all lgroups with same affinity together
*/
if (nlgrps_strong) {
if (nlgrps_weak || nlgrps_none)
}
if (nlgrps_weak) {
if (nlgrps_none)
}
if (nlgrps_none) {
}
}
/*
* Print heading for specified operation
*/
static void
{
switch (op) {
case PLGRP_AFFINITY_GET:
(void) printf(HDR_PLGRP_AFF_GET);
break;
case PLGRP_AFFINITY_SET:
(void) printf(HDR_PLGRP_AFF_SET);
break;
case PLGRP_HOME_GET:
(void) printf(HDR_PLGRP_HOME_GET);
break;
case PLGRP_HOME_SET:
(void) printf(HDR_PLGRP_HOME_SET);
break;
default:
break;
}
}
/*
* Use /proc to call lgrp_affinity_get() in another process
*/
static lgrp_affinity_t
{
int Pnargs;
int Pretval;
int syscall;
/*
* Fill in arguments needed for syscall(SYS_lgrpsys,
* LGRP_SYS_AFFINITY_GET, 0, &args)
*/
/*
* Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
* LGRP_SYS_AFFINITY_GET, idtype, id)
*/
Pargdp++;
Pargdp++;
Pargdp++;
/*
* Have agent LWP call syscall with appropriate arguments in target
* process
*/
if (Pretval) {
return (LGRP_AFF_INVALID);
}
}
/*
* Use /proc to call lgrp_affinity_set() in another process
*/
static int
{
int Pnargs;
int Pretval;
int syscall;
/*
* Fill in arguments needed for syscall(SYS_lgrpsys,
* LGRP_SYS_AFFINITY_SET, 0, &args)
*/
/*
* Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
* LGRP_SYS_AFFINITY_SET, idtype, id)
*/
Pargdp++;
Pargdp++;
Pargdp++;
/*
* Have agent LWP call syscall with appropriate arguments in
* target process
*/
if (Pretval) {
return (-1);
}
}
/*
* Use /proc to call lgrp_home() in another process
*/
static lgrp_id_t
{
int Pnargs;
int Pretval;
int syscall;
/*
* Fill in arguments needed for syscall(SYS_lgrpsys,
* LGRP_SYS_HOME, idtype, id)
*/
/*
* Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
* LGRP_SYS_HOME, idtype, id)
*/
Pargdp++;
Pargdp++;
Pargdp++;
/*
* Have agent LWP call syscall with appropriate arguments in
* target process
*/
if (Pretval) {
return (-1);
}
}
/*
* Use /proc to call lgrp_affinity_set(3LGRP) to set home lgroup of given
* thread
*/
static int
{
}
/*
* Do plgrp(1) operation on specified thread
*/
static int
const lwpsinfo_t *lwpsinfo)
{
int i;
int nlgrps;
int nchanged;
/*
* No args, so nothing to do.
*/
if (plgrp_args == NULL)
return (0);
/*
* Unpack plgrp(1) arguments and state needed to process this LWP
*/
switch (plgrp_args->op) {
case PLGRP_HOME_GET:
/*
* Get and display home lgroup for given LWP
*/
break;
case PLGRP_AFFINITY_GET:
/*
* Get and display this LWP's home lgroup and affinities
* for specified lgroups
*/
/*
* Collect affinity values
*/
for (i = 0; i < nlgrps; i++) {
lgrps[i]);
if (affs[i] == LGRP_AFF_INVALID) {
nerrors++;
gettext("%s: cannot get affinity"
" for lgroup %d for %d/%d: %s\n"),
}
}
/*
* Print affinities for each type.
*/
(void) printf("\n");
break;
case PLGRP_HOME_SET:
/*
* Get home lgroup before and after setting it and display
* change. If more than one lgroup and one LWP are specified,
* then home LWPs to lgroups in round robin fashion.
*/
i = plgrp_args->index;
nerrors++;
gettext("%s: cannot set home lgroup of %d/%d"
" to lgroup %d: %s\n"),
(void) printf("\n");
} else {
int len;
if (home < 0) {
gettext("%s cannot get home lgroup for"
" %d/%d: %s\n"),
nerrors++;
}
}
break;
case PLGRP_AFFINITY_SET:
/*
* Set affinities for specified lgroups and print old and new
* affinities and any resulting change in home lgroups
*/
/*
* Get initial home lgroup as it may change.
*/
/*
* Need to allocate arrays indexed by lgroup (ID) for
* affinities and lgroups because user may specify affinity
* for same lgroup multiple times....
*
* Keeping these arrays by lgroup (ID) eliminates any
* duplication and makes it easier to just print initial and
* final lgroup affinities (instead of trying to keep a list
* of lgroups specified which may include duplicates)
*/
lgrps_changed == NULL) {
progname);
nerrors++;
return (EXIT_NONFATAL);
}
/*
* Initialize current and initial lgroup affinities and
* lgroups changed
*/
if (!LGRP_VALID(lgrpid)) {
} else {
nerrors++;
gettext("%s: cannot get"
" affinity for lgroup %d"
" for %d/%d: %s\n"),
}
}
}
/*
* Change affinities.
*/
for (i = 0; i < nlgrps; i++) {
/*
* If the suggested affinity is the same as the current
* one, skip this lgroup.
*/
continue;
/*
* Set affinity to the new value
*/
aff) < 0) {
nerrors++;
gettext("%s: cannot set"
" %s affinity for lgroup %d"
" for %d/%d: %s\n"),
continue;
}
/*
* Get the new value and verify that it changed as
* expected.
*/
nerrors++;
gettext("%s: cannot get"
" affinity for lgroup %d"
" for %d/%d: %s\n"),
continue;
}
gettext("%s: affinity for"
" lgroup %d is set to %d instead of %d"
" for %d/%d\n"),
nerrors++;
}
}
/*
* Compare current and initial affinities and mark lgroups with
* changed affinities.
*/
nchanged = 0;
nchanged++;
}
}
if (nchanged == 0) {
/*
* Nothing changed, so just print current affinities for
* specified lgroups.
*/
for (i = 0; i < nlgrps; i++) {
}
(void) printf("%-*d",
(int)strlen(HDR_PLGRP_HOME_CHANGE),
(int)old_home);
(void) printf("\n");
} else {
/*
* Some lgroup affinities changed, so display old
* and new home lgroups for thread and its old and new
* affinities for affected lgroups
*/
if (home < 0) {
gettext("%s: cannot get home"
" for %d/%d: %s\n"),
nerrors++;
}
int len;
/*
* Fit string into fixed width
*/
} else {
}
/*
* Print change in affinities from old to new
*/
(void) printf(" => ");
(void) printf("\n");
}
break;
default:
break;
}
return (0);
}
/*
* Routine called by Plwp_iter_all() as it iterates through LWPs of another
* process
*/
/* ARGSUSED */
static int
const lwpsinfo_t *lwpsinfo)
{
/*
* Nothing to do if no arguments
*/
return (0);
/*
* Unpack plgrp(1) arguments and state needed to process this LWP
*/
plgrp_args = arg;
/*
* Just return if no /proc handle for process
*/
return (0);
/*
* Skip agent LWP and any LWPs that weren't specified
*/
return (0);
plgrp_args->nthreads++;
/*
* Do all plgrp(1) operations specified on given thread
*/
}
/*
* Get target process specified in "pidstring" argument to do operation(s)
* specified in "plgrp_todo" using /proc and agent LWP
*/
static void
{
int error;
const char *lwps;
/*
* Nothing to do, so return.
*/
return;
/*
* Grab target process or core and return
* /proc handle for process and string of LWP
* IDs
*/
gettext("%s: Unable to grab process %s: %s\n"),
nerrors++;
return;
}
/*
* Fill in remaining plgrp(1) arguments and state needed to do
* plgrp(1) operation(s) on desired LWPs in our handler
* called by Plwp_iter_all() as it iterates over LWPs
* in given process
*/
/*
* Iterate over LWPs in process and do specified
* operation(s) on those specified
*/
gettext("%s: error iterating over threads\n"),
progname);
nerrors++;
}
}
/*
* Parse command line and kick off any resulting actions
*
* plgrp(1) has the following command line syntax:
*
* plgrp [-h] <pid> | <core> [/lwps] ...
* plgrp [-F] -a <lgroup>,... <pid>[/lwps] ...
* plgrp [-F] -H <lgroup>,... <pid>[/lwps] ...
* plgrp [-F] -A <lgroup>,... [/none|weak|strong] ... <pid>[/lwps] ...
*
* where <lgroup> is an lgroup ID, "all", "root", "leaves".
*/
int
{
char *affstring;
int c;
int Fflag;
int i;
int opt_seen;
char *s;
(void) textdomain(TEXT_DOMAIN);
opt_seen = 0;
/*
* Get name of program
*/
/*
* Not much to do when only name of program given
*/
if (argc == 1)
usage(0);
/*
* Catch signals from terminal, so they can be handled asynchronously
* when we're ready instead of when we're not (;-)
*/
/*
* Take snapshot of lgroup hierarchy
*/
if (cookie == LGRP_COOKIE_NONE) {
gettext("%s: Fatal error: cannot get lgroup"
" information from the OS: %s\n"),
return (EXIT_FAILURE);
}
/*
* Remember arguments and state needed to do plgrp(1) operation
* on desired LWPs
*/
/*
* Parse options
*/
opterr = 0;
Fflag = 0;
/*
* Parse option and only allow one option besides -F to be
* specified
*/
switch (c) {
case 'h': /* Get home lgroup */
/*
* Only allow one option (besides -F) to be specified
*/
if (opt_seen)
opt_seen = 1;
break;
case 'H': /* Set home lgroup */
/*
* Fail if already specified option (besides -F)
* or no more arguments
*/
}
opt_seen = 1;
/* If there are no valid lgroups exit immediately */
if (plgrp_todo.nlgrps == 0) {
gettext("%s: no valid lgroups"
" specified for -%c\n\n"),
progname, c);
}
break;
case 'a': /* Get lgroup affinity */
/*
* Fail if already specified option (besides -F)
* or no more arguments
*/
}
opt_seen = 1;
/* If there are no valid lgroups exit immediately */
if (plgrp_todo.nlgrps == 0) {
gettext("%s: no valid lgroups specified"
" for -%c\n\n"),
progname, c);
}
break;
case 'A': /* Set lgroup affinity */
/*
* Fail if already specified option (besides -F)
* or no more arguments
*/
}
opt_seen = 1;
/*
* 'affstring' is the unparsed prtion of the affinity
* specification like 1,2/none,2/weak,0/strong
*
* 'next' is the next affinity specification to parse.
*/
char *next;
/*
* affstring points to the first affinity
* specification. Split the string by
* DELIMIT_AFF separator and parse lgroups and
* affinity value separately.
*/
if (s == NULL) {
gettext("%s: invalid "
"syntax >%s<\n"),
}
if (aff == LGRP_AFF_INVALID) {
gettext("%s: invalid "
"affinity >%s<\n"),
}
/*
* next should either point to the empty string
* or to the DELIMIT_AFF_LST separator.
*/
if (*next != '\0') {
if (*next != DELIMIT_AFF_LST) {
gettext("%s: invalid "
"syntax >%s<\n"),
}
*next = '\0';
next++;
}
/*
* Now parse the list of lgroups
*/
affstring) < 0) {
}
/*
* Set desired affinity for specified lgroup to
* the specified affinity.
*/
for (i = 0; i < plgrp_todo.nlgrps; i++) {
if (plgrp_todo.affs[i] ==
}
/*
* We processed the leftmost element of the
* list. Advance affstr to the remaining part of
* the list. and repeat.
*/
}
/*
* If there are no valid lgroups, exit immediately
*/
if (plgrp_todo.nlgrps == 0) {
gettext("%s: no valid lgroups specified "
"for -%c\n\n"), progname, c);
}
break;
case 'F': /* Force */
/*
* Only allow one occurrence
*/
if (Fflag != 0) {
}
/*
* Set flag to force /proc to grab process even though
* it's been grabbed by another process already
*/
Fflag = PGRAB_FORCE;
break;
case '?': /* Unrecognized option */
default:
break;
}
}
/*
* Should have more arguments left at least for PID or core
*/
/*
* Print heading and process each [pid | core]/lwps argument
*/
(void) proc_initstdio();
(void) proc_flushstdio();
}
(void) proc_finistdio();
if (plgrp_todo.nthreads == 0) {
progname);
}
}