Home | History | Annotate | Download | only in fptest
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <kstat.h>
     28 #include <libnvpair.h>
     29 #include <libsysevent.h>
     30 #include <stdarg.h>
     31 #include <stdio.h>
     32 #include <string.h>
     33 #include <sys/fm/protocol.h>
     34 #include <sys/fm/util.h>
     35 #include <sys/types.h>
     36 #include <sys/processor.h>
     37 #include <unistd.h>
     38 #include <fp.h>
     39 #include <fps_defines.h>
     40 #include <fps_ereport.h>
     41 #include <fpst-defines.h>
     42 
     43 #define	CLASS_HEAD "ereport.cpu"
     44 #define	CLASS_TAIL "fpu.fpscrub"
     45 
     46 /* nvlist */
     47 static nvlist_t *fps_nvlist_create();
     48 
     49 /* ereport piece generators */
     50 static int fps_fmri_cpu_set(nvlist_t *fmri_cpu, uint32_t cpu_id);
     51 static int fps_fmri_svc_set(nvlist_t *fmri_svc, const char *svc_fmri);
     52 static int fps_post_ereport(nvlist_t *ereport);
     53 static uint64_t fps_ena_generate(uint64_t timestamp, uint32_t cpuid,
     54 		uchar_t format);
     55 
     56 /* cpu check and name convert */
     57 static char *fps_get_cpu_brand(uint32_t cpu_id);
     58 static char *fps_convert_cpu_brand(char *brand);
     59 
     60 /* ereport struct functions */
     61 int fps_generate_ereport_struct(struct fps_test_ereport *report);
     62 void setup_fps_test_struct(int mask, struct fps_test_ereport *rep, ...);
     63 void initialize_fps_test_struct(struct fps_test_ereport *init_me);
     64 
     65 /*
     66  * fps_nvlist_create() allocates the memory for an
     67  * nvlist.
     68  */
     69 static nvlist_t *
     70 fps_nvlist_create()
     71 {
     72 	int nr_malloc;
     73 	nvlist_t *nvl;
     74 	struct timeval timeout;
     75 
     76 	timeout.tv_sec = 0;
     77 	timeout.tv_usec = 10000;
     78 	nr_malloc = 0;
     79 
     80 	nvl = NULL;
     81 	(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
     82 
     83 	while (nvl == NULL && nr_malloc < 10) {
     84 		(void) select(1, NULL, NULL, NULL, &timeout);
     85 		(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
     86 		nr_malloc++;
     87 	}
     88 
     89 	return (nvl);
     90 }
     91 
     92 /*
     93  * fps_ena_generate(uint64_t timestamp, processorid_t cpuid,
     94  * uchar_t format)creates the ENA for the ereport.
     95  */
     96 static uint64_t
     97 fps_ena_generate(uint64_t timestamp, uint32_t cpuid, uchar_t format)
     98 {
     99 	uint64_t ena = 0;
    100 
    101 	switch (format) {
    102 	case FM_ENA_FMT1:
    103 		if (timestamp) {
    104 			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
    105 			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
    106 			    ENA_FMT1_CPUID_MASK) |
    107 			    ((timestamp << ENA_FMT1_TIME_SHFT) &
    108 			    ENA_FMT1_TIME_MASK));
    109 		} else {
    110 			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
    111 			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
    112 			    ENA_FMT1_CPUID_MASK) |
    113 			    ((gethrtime() << ENA_FMT1_TIME_SHFT) &
    114 			    ENA_FMT1_TIME_MASK));
    115 		}
    116 		break;
    117 	case FM_ENA_FMT2:
    118 		ena = (uint64_t)((format & ENA_FORMAT_MASK) |
    119 		    ((timestamp << ENA_FMT2_TIME_SHFT) & ENA_FMT2_TIME_MASK));
    120 		break;
    121 	default:
    122 		break;
    123 	}
    124 
    125 	return (ena);
    126 }
    127 
    128 /*
    129  * fps_fmri_svc_set(nvlist_t *fmri_svc, const char *svc_fmri)
    130  * adds the detector data to fmri_svc.
    131  */
    132 static int
    133 fps_fmri_svc_set(nvlist_t *fmri_svc, const char *svc_fmri)
    134 {
    135 	if (fmri_svc == NULL)
    136 		return (1);
    137 
    138 	if (svc_fmri == NULL)
    139 		return (1);
    140 
    141 	if (nvlist_add_uint8(fmri_svc, FM_VERSION, FM_SVC_SCHEME_VERSION) != 0)
    142 		return (1);
    143 
    144 	if (nvlist_add_string(fmri_svc, FM_FMRI_SCHEME,
    145 	    FM_FMRI_SCHEME_SVC) != 0)
    146 		return (1);
    147 
    148 	if (nvlist_add_string(fmri_svc, FM_FMRI_SVC_NAME,
    149 	    svc_fmri) != 0)
    150 		return (1);
    151 
    152 	return (0);
    153 }
    154 
    155 /*
    156  * fps_fmri_cpu_set(nvlist_t *fmri_cpu, uint32_t cpu_id)
    157  * adds the resource data to fmri_cpu.
    158  */
    159 static int
    160 fps_fmri_cpu_set(nvlist_t *fmri_cpu, uint32_t cpu_id)
    161 {
    162 	if (fmri_cpu == NULL)
    163 		return (1);
    164 
    165 	if (nvlist_add_uint8(fmri_cpu, FM_VERSION,
    166 	    FM_CPU_SCHEME_VERSION) != 0)
    167 		return (1);
    168 
    169 	if (nvlist_add_string(fmri_cpu, FM_FMRI_SCHEME,
    170 	    FM_FMRI_SCHEME_CPU) != 0)
    171 		return (1);
    172 
    173 	if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0)
    174 		return (1);
    175 	return (0);
    176 }
    177 
    178 /*
    179  * fps_post_ereport(nvlist_t *ereport) posts an
    180  * ereport to the sysevent error channel.  The error
    181  * channel is assumed to be established by fps-transport.so.
    182  */
    183 static int
    184 fps_post_ereport(nvlist_t *ereport)
    185 {
    186 	evchan_t *scp;
    187 
    188 	if (sysevent_evc_bind(CHANNEL, &scp, BIND_FLAGS) != 0) {
    189 		return (1);
    190 	}
    191 
    192 	if (sysevent_evc_publish(scp, CLASS, SUBCLASS, VENDOR,
    193 	    PUBLISHER, ereport, EVCH_NOSLEEP) != 0) {
    194 		return (1);
    195 	}
    196 
    197 	(void) sleep(1);
    198 
    199 	(void) fflush(NULL);
    200 	(void) sysevent_evc_unbind(scp);
    201 
    202 	return (0);
    203 }
    204 /*
    205  * fps_convert_cpu_brand(char *brand) changes
    206  * the kstat data to match the ereport class
    207  * names.
    208  */
    209 static char *
    210 fps_convert_cpu_brand(char *brand)
    211 {
    212 	if (brand == NULL)
    213 		return (NULL);
    214 
    215 	if (strcasecmp(brand, USIII_KSTAT) == 0)
    216 		return (USIII);
    217 	else if (strcasecmp(brand, USIIIi_KSTAT) == 0)
    218 		return (USIIIi);
    219 	else if (strcasecmp(brand, USIIIP_KSTAT) == 0)
    220 		return (USIIIP);
    221 	else if (strcasecmp(brand, USIV_KSTAT) == 0)
    222 		return (USIV);
    223 	else if (strcasecmp(brand, USIVP_KSTAT) == 0)
    224 		return (USIVP);
    225 	else
    226 		return (NULL);
    227 }
    228 
    229 /*
    230  * get_cpu_brand(uint32_t cpu_id)gets the
    231  * brand of the CPU and returns the CPU
    232  * name to use in the ereport class name.
    233  */
    234 static char *
    235 fps_get_cpu_brand(uint32_t cpu_id)
    236 {
    237 	char *brand;
    238 	kstat_ctl_t *kc;
    239 	kstat_t *ksp;
    240 	kstat_named_t *knp;
    241 
    242 	kc = kstat_open();
    243 	if (kc == NULL) {
    244 		return (NULL);
    245 	}
    246 
    247 	if ((ksp = kstat_lookup(kc, "cpu_info", (int)cpu_id, NULL)) == NULL) {
    248 		(void) kstat_close(kc);
    249 		return (NULL);
    250 	}
    251 
    252 	if ((kstat_read(kc, ksp, NULL)) == -1) {
    253 		(void) kstat_close(kc);
    254 		return (NULL);
    255 	}
    256 
    257 	if ((knp = kstat_data_lookup(ksp, "brand")) == NULL) {
    258 		(void) kstat_close(kc);
    259 		return (NULL);
    260 	}
    261 
    262 	brand = fps_convert_cpu_brand(KSTAT_NAMED_STR_PTR(knp));
    263 	(void) kstat_close(kc);
    264 
    265 	if (brand == NULL)
    266 		return (NULL);
    267 
    268 	return (brand);
    269 }
    270 
    271 /*
    272  * fps_generate_ereport_struct(struct fps_test_ereport *report)
    273  * takes report and constructs an nvlist that will be used
    274  * for the ereport.
    275  */
    276 int
    277 fps_generate_ereport_struct(struct fps_test_ereport *report)
    278 {
    279 	char class_name[FM_MAX_CLASS];
    280 	char *cpu_brand;
    281 	char *string_data;
    282 	int expect_size;
    283 	int is_valid_cpu;
    284 	int mask;
    285 	int observe_size;
    286 	int ret;
    287 	nvlist_t *detector;
    288 	nvlist_t *ereport;
    289 	nvlist_t *resource;
    290 	uint32_t cpu_id;
    291 	uint32_t test;
    292 	uint8_t fps_ver;
    293 	uint64_t ena;
    294 	uint64_t ereport_time;
    295 	uint64_t *expect;
    296 	uint64_t *observe;
    297 
    298 	if (report == NULL)
    299 		return (FPU_EREPORT_FAIL);
    300 
    301 	ret = FPU_FOROFFLINE;
    302 	cpu_id = report->cpu_id;
    303 	test = report->test_id;
    304 	mask = report->mask;
    305 	is_valid_cpu = report->is_valid_cpu;
    306 	expect_size = report->expected_size;
    307 	expect = report->expected;
    308 	observe_size = report->observed_size;
    309 	observe = report->observed;
    310 	string_data = report->info;
    311 
    312 	/* allocate nvlists */
    313 	if ((ereport = fps_nvlist_create()) == NULL)
    314 		_exit(FPU_EREPORT_FAIL);
    315 
    316 	if ((detector = fps_nvlist_create()) == NULL) {
    317 		_exit(FPU_EREPORT_FAIL);
    318 	}
    319 
    320 	/* setup class */
    321 	if ((cpu_brand = fps_get_cpu_brand(cpu_id)) == NULL)
    322 		_exit(FPU_EREPORT_FAIL);
    323 
    324 	if ((snprintf(class_name, FM_MAX_CLASS, "%s.%s.%s",
    325 	    CLASS_HEAD, cpu_brand, CLASS_TAIL)) < 0)
    326 		_exit(FPU_EREPORT_FAIL);
    327 
    328 	/* setup ena */
    329 	ereport_time = gethrtime();
    330 	ena = fps_ena_generate(ereport_time, cpu_id, FM_ENA_FMT1);
    331 
    332 	/* setup detector */
    333 	if (fps_fmri_svc_set(detector, getenv("SMF_FMRI")) != 0) {
    334 		_exit(FPU_EREPORT_FAIL);
    335 	}
    336 
    337 	/* setup fps-version */
    338 	fps_ver = FPS_VERSION;
    339 
    340 	/* setup resource */
    341 	if (is_valid_cpu) {
    342 		resource = fps_nvlist_create();
    343 
    344 		if (fps_fmri_cpu_set(resource, cpu_id)) {
    345 			_exit(FPU_EREPORT_FAIL);
    346 		}
    347 	} else {
    348 		resource = NULL;
    349 	}
    350 
    351 	/* put it together */
    352 	if (nvlist_add_string(ereport, NAME_FPS_CLASS, class_name) != 0)
    353 		_exit(FPU_EREPORT_FAIL);
    354 
    355 	if (ena != 0) {
    356 		if (nvlist_add_uint64(ereport, NAME_FPS_ENA, ena) != 0)
    357 			_exit(FPU_EREPORT_FAIL);
    358 	} else
    359 		_exit(FPU_EREPORT_FAIL);
    360 
    361 	if (nvlist_add_nvlist(ereport, NAME_FPS_DETECTOR,
    362 	    (nvlist_t *)detector) != 0)
    363 		_exit(FPU_EREPORT_FAIL);
    364 
    365 	if (nvlist_add_uint8(ereport, NAME_FPS_VERSION, fps_ver) != 0)
    366 		_exit(FPU_EREPORT_FAIL);
    367 
    368 	if (nvlist_add_uint32(ereport, NAME_FPS_TEST_ID, test) != 0)
    369 		ret = FPU_EREPORT_INCOM;
    370 
    371 	if (nvlist_add_uint64_array(ereport, NAME_FPS_EXPECTED_VALUE,
    372 	    expect, expect_size) != 0)
    373 		ret = FPU_EREPORT_INCOM;
    374 
    375 	if (nvlist_add_uint64_array(ereport, NAME_FPS_OBSERVED_VALUE,
    376 	    observe, observe_size) != 0)
    377 		ret = FPU_EREPORT_INCOM;
    378 
    379 	if (mask & IS_EREPORT_INFO) {
    380 		if (nvlist_add_string(ereport, NAME_FPS_STRING_DATA,
    381 		    string_data) != 0)
    382 			ret = FPU_EREPORT_INCOM;
    383 	}
    384 
    385 	if (is_valid_cpu) {
    386 		if (nvlist_add_nvlist(ereport, NAME_FPS_RESOURCE,
    387 		    (nvlist_t *)resource) != 0)
    388 			_exit(FPU_EREPORT_FAIL);
    389 	}
    390 
    391 	/* publish */
    392 	if (fps_post_ereport(ereport)) {
    393 		ret = FPU_EREPORT_FAIL;
    394 	}
    395 
    396 	/* free nvlists */
    397 	nvlist_free(ereport);
    398 
    399 	if (resource != NULL)
    400 		nvlist_free(resource);
    401 
    402 	if (detector != NULL)
    403 		nvlist_free(detector);
    404 
    405 	return (ret);
    406 }
    407 
    408 /*
    409  * initialize_fps_test_struct(struct fps_test_ereport *init_me)
    410  * creates the initial values for the init_me.
    411  */
    412 void
    413 initialize_fps_test_struct(struct fps_test_ereport *init_me)
    414 {
    415 	if (init_me == NULL)
    416 		return;
    417 
    418 	init_me->cpu_id = 0;
    419 	init_me->test_id = 0;
    420 	init_me->observed_size = 0;
    421 	init_me->expected_size = 0;
    422 	init_me->is_valid_cpu = 1;
    423 	init_me->info[0] = '\0';
    424 	init_me->mask = NO_EREPORT_INFO;
    425 }
    426 
    427 /*
    428  * setup_fps_test_struct(int mask, struct fps_test_ereport *rep,
    429  * ...) takes a variable amount of input and stores it in rep
    430  * based on mask provided.
    431  */
    432 void
    433 setup_fps_test_struct(int mask, struct fps_test_ereport *rep, ...)
    434 {
    435 	char *data;
    436 	int i;
    437 	uint64_t *exp_arg;
    438 	uint64_t *obs_arg;
    439 	va_list argptr;
    440 
    441 	if (rep == NULL)
    442 		return;
    443 
    444 	/* begin parsing args */
    445 	va_start(argptr, rep);
    446 
    447 	/* test id */
    448 	rep->test_id = va_arg(argptr, int);
    449 
    450 	/* observed */
    451 	obs_arg = va_arg(argptr, uint64_t *);
    452 
    453 	/* expected */
    454 	exp_arg = va_arg(argptr, uint64_t *);
    455 
    456 	/* observed size */
    457 	rep->observed_size = va_arg(argptr, int);
    458 
    459 	/* expected size */
    460 	rep->expected_size = va_arg(argptr, int);
    461 
    462 	/* copy arrays of observed and expected */
    463 	if (rep->observed_size < 1 || rep->expected_size < 1)
    464 		return;
    465 
    466 	if (obs_arg == NULL || exp_arg == NULL)
    467 		return;
    468 
    469 	for (i = 0; i < rep->observed_size; i++)
    470 		rep->observed[i] = obs_arg[i];
    471 
    472 	for (i = 0; i < rep->expected_size; i++)
    473 		rep->expected[i] = exp_arg[i];
    474 
    475 	rep->mask = mask;
    476 
    477 	/* copy string data if there */
    478 	if (mask & IS_EREPORT_INFO)	{
    479 		data = va_arg(argptr, char *);
    480 
    481 		if (data == NULL) {
    482 			va_end(argptr);
    483 
    484 			return;
    485 		}
    486 
    487 		(void) strlcpy(rep->info, data, MAX_INFO_SIZE-1);
    488 	}
    489 
    490 	va_end(argptr);
    491 }
    492