131e37bbvn/*
231e37bbvn * CDDL HEADER START
331e37bbvn *
431e37bbvn * The contents of this file are subject to the terms of the
531e37bbvn * Common Development and Distribution License (the "License").
631e37bbvn * You may not use this file except in compliance with the License.
731e37bbvn *
831e37bbvn * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
931e37bbvn * or http://www.opensolaris.org/os/licensing.
1031e37bbvn * See the License for the specific language governing permissions
1131e37bbvn * and limitations under the License.
1231e37bbvn *
1331e37bbvn * When distributing Covered Code, include this CDDL HEADER in each
1431e37bbvn * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1531e37bbvn * If applicable, add the following below this CDDL HEADER, with the
1631e37bbvn * fields enclosed by brackets "[]" replaced with your own identifying
1731e37bbvn * information: Portions Copyright [yyyy] [name of copyright owner]
1831e37bbvn *
1931e37bbvn * CDDL HEADER END
2031e37bbvn */
2131e37bbvn/*
225f149bccy * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2331e37bbvn * Use is subject to license terms.
2431e37bbvn */
2531e37bbvn
2631e37bbvn#include <cma.h>
2731e37bbvn
285f149bccy#include <sys/fm/ldom.h>
295f149bccy#include <sys/fm/protocol.h>
303f1e69bCheng Sean Ye#include <fm/fmd_fmri.h>
313f1e69bCheng Sean Ye#include <fm/libtopo.h>
325f149bccy
335f149bccy#include <assert.h>
3431e37bbvn#include <fcntl.h>
3531e37bbvn#include <unistd.h>
3631e37bbvn#include <errno.h>
375f149bccy#include <strings.h>
385f149bccy
395f149bccy#include <sys/types.h>
4031e37bbvn#include <sys/processor.h>
4131e37bbvn
425f149bccyextern ldom_hdl_t *cma_lhp;
435f149bccy
445f149bccy/*ARGSUSED*/
455f149bccyint
465f149bccycpu_blacklist_cmd(fmd_hdl_t *hdl, nvlist_t *fmri, boolean_t repair)
475f149bccy{
485f149bccy	if (repair)
495f149bccy		return (ldom_fmri_unblacklist(cma_lhp, fmri));
505f149bccy	else
515f149bccy		return (ldom_fmri_blacklist(cma_lhp, fmri));
525f149bccy}
535f149bccy
545f149bccyint
555f149bccycma_cpu_blacklist(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t *asru,
565f149bccy    boolean_t repair)
5731e37bbvn{
5831e37bbvn	nvlist_t *fmri;
5931e37bbvn	int rc, err;
6031e37bbvn
6131e37bbvn	/*
625f149bccy	 * Some platforms have special unums for the E$ DIMMs.	If we're dealing
6331e37bbvn	 * with a platform that has these unums, one will have been added to the
6431e37bbvn	 * fault as the resource.  We'll use that for the blacklisting.  If we
6531e37bbvn	 * can't find a resource, we'll fall back to the ASRU.
6631e37bbvn	 */
6731e37bbvn	if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &fmri) != 0)
6831e37bbvn		fmri = asru;
6931e37bbvn
705f149bccy	rc = cpu_blacklist_cmd(hdl, fmri, repair);
7131e37bbvn	err = errno;
7231e37bbvn
7331e37bbvn	if (rc < 0 && err != ENOTSUP) {
7431e37bbvn		errno = err;
7531e37bbvn		return (-1);
7631e37bbvn	}
7731e37bbvn
7831e37bbvn	return (0);
7931e37bbvn}
8031e37bbvn
815f149bccy/*ARGSUSED*/
825f149bccystatic int
835f149bccycpu_cmd(fmd_hdl_t *hdl, nvlist_t *fmri, int cmd)
8431e37bbvn{
855f149bccy	int rc = 0;
863f1e69bCheng Sean Ye	char *scheme;
873f1e69bCheng Sean Ye
883f1e69bCheng Sean Ye	/*
893f1e69bCheng Sean Ye	 * We're using topo retire if the fmri is in "hc" scheme.
903f1e69bCheng Sean Ye	 */
913f1e69bCheng Sean Ye	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) == 0 &&
923f1e69bCheng Sean Ye	    strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) {
933f1e69bCheng Sean Ye		if (cmd != P_STATUS) {
943f1e69bCheng Sean Ye			errno = EINVAL;
953f1e69bCheng Sean Ye			return (-1);
963f1e69bCheng Sean Ye		}
973f1e69bCheng Sean Ye		rc = fmd_nvl_fmri_service_state(hdl, fmri);
983f1e69bCheng Sean Ye		switch (rc) {
993f1e69bCheng Sean Ye		case FMD_SERVICE_STATE_UNUSABLE:
1003f1e69bCheng Sean Ye			return (P_FAULTED);
1013f1e69bCheng Sean Ye		case -1:
1023f1e69bCheng Sean Ye			return (-1);
1033f1e69bCheng Sean Ye		default:
1043f1e69bCheng Sean Ye			return (P_ONLINE);
1053f1e69bCheng Sean Ye		}
1063f1e69bCheng Sean Ye	}
1075f149bccy
1085f149bccy	switch (cmd & ~P_FORCED) {
1095f149bccy	case P_STATUS:
1105f149bccy		rc = ldom_fmri_status(cma_lhp, fmri);
1115f149bccy		break;
1125f149bccy	case P_FAULTED:
1135f149bccy		rc = ldom_fmri_retire(cma_lhp, fmri);
1145f149bccy		break;
1155f149bccy	case P_ONLINE:
1165f149bccy		rc = ldom_fmri_unretire(cma_lhp, fmri);
1175f149bccy		break;
1185f149bccy	default:
1195f149bccy		errno = EINVAL;
1205f149bccy		return (-1);
1215f149bccy	}
1225f149bccy
1235f149bccy	if (rc != P_OFFLINE && rc != P_ONLINE && rc != P_FAULTED) {
1245f149bccy		errno = rc;
1255f149bccy		return (-1);
1265f149bccy	}
1275f149bccy
1285f149bccy	return (rc);
12931e37bbvn}
13031e37bbvn
1313f1e69bCheng Sean Yevoid
1323f1e69bCheng Sean Yecma_cpu_start_retry(fmd_hdl_t *hdl, nvlist_t *fmri, const char *uuid,
1333f1e69bCheng Sean Ye    boolean_t repair)
1343f1e69bCheng Sean Ye{
1353f1e69bCheng Sean Ye	cma_cpu_t *cpu;
1363f1e69bCheng Sean Ye	char *scheme;
1373f1e69bCheng Sean Ye	uint_t cpuid;
1383f1e69bCheng Sean Ye	nvlist_t *asru = NULL;
1393f1e69bCheng Sean Ye	topo_hdl_t *thp;
1403f1e69bCheng Sean Ye	int err;
1413f1e69bCheng Sean Ye
1423f1e69bCheng Sean Ye	if (repair || nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
1433f1e69bCheng Sean Ye		return;
1443f1e69bCheng Sean Ye	if (strcmp(scheme, FM_FMRI_SCHEME_CPU) == 0) {
1453f1e69bCheng Sean Ye		if (nvlist_lookup_uint32(fmri, FM_FMRI_CPU_ID, &cpuid) != 0)
1463f1e69bCheng Sean Ye			return;
1473f1e69bCheng Sean Ye	} else if (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) {
1483f1e69bCheng Sean Ye		return;
1493f1e69bCheng Sean Ye	} else {
1503f1e69bCheng Sean Ye		/* lookup cpuid from ASRU */
1513f1e69bCheng Sean Ye		thp = fmd_fmri_topo_hold(TOPO_VERSION);
1523f1e69bCheng Sean Ye		if (thp != NULL) {
1533f1e69bCheng Sean Ye			(void) topo_fmri_asru(thp, fmri, &asru, &err);
1543f1e69bCheng Sean Ye			fmd_fmri_topo_rele(thp);
1553f1e69bCheng Sean Ye		}
1563f1e69bCheng Sean Ye		if (nvlist_lookup_uint32(asru, FM_FMRI_CPU_ID, &cpuid) != 0) {
1573f1e69bCheng Sean Ye			nvlist_free(asru);
1583f1e69bCheng Sean Ye			return;
1593f1e69bCheng Sean Ye		}
1603f1e69bCheng Sean Ye	}
1613f1e69bCheng Sean Ye
1623f1e69bCheng Sean Ye	/*
1633f1e69bCheng Sean Ye	 * check to see if the cpu has been offline.
1643f1e69bCheng Sean Ye	 */
1653f1e69bCheng Sean Ye	fmd_hdl_debug(hdl, "cpu %u is not offline yet - sleeping\n", cpuid);
1663f1e69bCheng Sean Ye
1673f1e69bCheng Sean Ye	/*
1683f1e69bCheng Sean Ye	 * Create a cpu node and add to the head of the cpu list
1693f1e69bCheng Sean Ye	 */
1703f1e69bCheng Sean Ye	cpu = fmd_hdl_zalloc(hdl, sizeof (cma_cpu_t), FMD_SLEEP);
1713f1e69bCheng Sean Ye	(void) nvlist_dup(fmri, &cpu->cpu_fmri, 0);
1723f1e69bCheng Sean Ye	if (uuid != NULL)
1733f1e69bCheng Sean Ye		cpu->cpu_uuid = fmd_hdl_strdup(hdl, uuid, FMD_SLEEP);
1743f1e69bCheng Sean Ye
1753f1e69bCheng Sean Ye	cpu->cpuid = cpuid;
1763f1e69bCheng Sean Ye	cpu->cpu_next = cma.cma_cpus;
1773f1e69bCheng Sean Ye	cma.cma_cpus = cpu;
1783f1e69bCheng Sean Ye
1793f1e69bCheng Sean Ye	if (cma.cma_cpu_timerid != 0)
1803f1e69bCheng Sean Ye		fmd_timer_remove(hdl, cma.cma_cpu_timerid);
1813f1e69bCheng Sean Ye
1823f1e69bCheng Sean Ye	cma.cma_cpu_curdelay = cma.cma_cpu_mindelay;
1833f1e69bCheng Sean Ye
1843f1e69bCheng Sean Ye	cma.cma_cpu_timerid =
1853f1e69bCheng Sean Ye	    fmd_timer_install(hdl, NULL, NULL, cma.cma_cpu_curdelay);
1863f1e69bCheng Sean Ye}
1873f1e69bCheng Sean Ye
1885f149bccy
18931e37bbvnint
1905f149bccycma_cpu_statechange(fmd_hdl_t *hdl, nvlist_t *asru, const char *uuid,
1915f149bccy    int cpustate, boolean_t repair)
19231e37bbvn{
1935f149bccy	int i;
19431e37bbvn	uint_t cpuid;
19531e37bbvn
1965f149bccy	if (nvlist_lookup_uint32(asru, FM_FMRI_CPU_ID, &cpuid) != 0) {
1975f149bccy		fmd_hdl_debug(hdl, "missing '%s'\n", FM_FMRI_CPU_ID);
19831e37bbvn		cma_stats.bad_flts.fmds_value.ui64++;
19931e37bbvn		return (CMA_RA_FAILURE);
20031e37bbvn	}
20131e37bbvn
2025f149bccy	/*
2035f149bccy	 * cpu offlining using ldom_fmri_retire() may be asynchronous, so we
2045f149bccy	 * have to set the timer and check the cpu status later.
2055f149bccy	 */
2065f149bccy	for (i = 0; i < cma.cma_cpu_tries;
2075f149bccy	    i++, (void) nanosleep(&cma.cma_cpu_delay, NULL)) {
2085f149bccy		if (cpu_cmd(hdl, asru, cpustate) != -1) {
2095f149bccy			if (repair)
2105f149bccy				cma_stats.cpu_repairs.fmds_value.ui64++;
2115f149bccy			else
2125f149bccy				cma_stats.cpu_flts.fmds_value.ui64++;
2135f149bccy			break;
2145f149bccy		}
21531e37bbvn	}
21631e37bbvn
2175f149bccy	if (i >= cma.cma_cpu_tries) {
2185f149bccy		cma_stats.cpu_fails.fmds_value.ui64++;
2195f149bccy	}
22031e37bbvn
2213f1e69bCheng Sean Ye	cma_cpu_start_retry(hdl, asru, uuid, repair);
2225f149bccy
2235f149bccy	return (CMA_RA_FAILURE);
22431e37bbvn}
22531e37bbvn
22631e37bbvnstatic int
22731e37bbvncpu_retry(fmd_hdl_t *hdl, cma_cpu_t *cpu)
22831e37bbvn{
22931e37bbvn	int rc = 0;
23031e37bbvn
23131e37bbvn	fmd_hdl_debug(hdl, "cpu_retry()\n");
23231e37bbvn
23331e37bbvn	if (cpu->cpu_fmri == NULL) {
23431e37bbvn		return (1);
23531e37bbvn	}
23631e37bbvn
23731e37bbvn	if (!fmd_nvl_fmri_present(hdl, cpu->cpu_fmri)) {
23831e37bbvn		fmd_hdl_debug(hdl, "cpu %u is not present", cpu->cpuid);
23931e37bbvn		return (1);
24031e37bbvn	}
24131e37bbvn
24231e37bbvn	rc = cpu_cmd(hdl, cpu->cpu_fmri, P_STATUS);
24331e37bbvn	if (rc == P_FAULTED || rc == P_OFFLINE) {
24431e37bbvn		fmd_hdl_debug(hdl, "cpu %u is offlined on retry %u\n",
24531e37bbvn		    cpu->cpuid, cpu->cpu_nretries);
24631e37bbvn		cma_stats.cpu_flts.fmds_value.ui64++;
24731e37bbvn
24831e37bbvn		if (cpu->cpu_uuid != NULL)
24931e37bbvn			fmd_case_uuclose(hdl, cpu->cpu_uuid);
25031e37bbvn		return (1); /* success */
25131e37bbvn	}
25231e37bbvn
25331e37bbvn	if (rc == -1) {
25431e37bbvn		fmd_hdl_debug(hdl, "failed to retry cpu %u\n", cpu->cpuid);
25531e37bbvn		cma_stats.page_fails.fmds_value.ui64++;
25631e37bbvn		return (1); /* give up */
25731e37bbvn	}
25831e37bbvn
25931e37bbvn	return (0);
26031e37bbvn}
26131e37bbvn
2625f149bccystatic void
2635f149bccycma_cpu_free(fmd_hdl_t *hdl, cma_cpu_t *cpu)
2645f149bccy{
265aab83bbJosef 'Jeff' Sipek	nvlist_free(cpu->cpu_fmri);
2665f149bccy	if (cpu->cpu_uuid != NULL)
2675f149bccy		fmd_hdl_strfree(hdl, cpu->cpu_uuid);
2685f149bccy	fmd_hdl_free(hdl, cpu, sizeof (cma_cpu_t));
2695f149bccy}
2705f149bccy
27131e37bbvnvoid
27231e37bbvncma_cpu_retry(fmd_hdl_t *hdl)
27331e37bbvn{
27431e37bbvn	cma_cpu_t **cpup;
27531e37bbvn
27631e37bbvn	fmd_hdl_debug(hdl, "cma_cpu_retry: timer fired\n");
27731e37bbvn
27831e37bbvn	cma.cma_cpu_timerid = 0;
27931e37bbvn
28031e37bbvn	cpup = &cma.cma_cpus;
28131e37bbvn	while (*cpup != NULL) {
28231e37bbvn		cma_cpu_t *cpu = *cpup;
28331e37bbvn
28431e37bbvn		if (cpu_retry(hdl, cpu)) {
28531e37bbvn			/*
28631e37bbvn			 * Successful retry or we're giving up - remove from
28731e37bbvn			 * the list
28831e37bbvn			 */
28931e37bbvn			*cpup = cpu->cpu_next;
29031e37bbvn
29131e37bbvn			cma_cpu_free(hdl, cpu);
29231e37bbvn		} else {
29331e37bbvn			cpu->cpu_nretries++;
29431e37bbvn			cpup = &cpu->cpu_next;
29531e37bbvn		}
29631e37bbvn	}
29731e37bbvn
29831e37bbvn	if (cma.cma_cpus == NULL)
29931e37bbvn		return; /* no more cpus */
30031e37bbvn
30131e37bbvn	/*
30231e37bbvn	 * We still have cpus to check.  Back the delay
30331e37bbvn	 * off, and schedule a retry.
30431e37bbvn	 */
30531e37bbvn	cma.cma_cpu_curdelay = MIN(cma.cma_cpu_curdelay * 2,
30631e37bbvn	    cma.cma_cpu_maxdelay);
30731e37bbvn
30831e37bbvn	fmd_hdl_debug(hdl, "scheduled cpu offline retry for %llu secs\n",
30931e37bbvn	    (u_longlong_t)(cma.cma_cpu_curdelay / NANOSEC));
31031e37bbvn
31131e37bbvn	cma.cma_cpu_timerid =
31231e37bbvn	    fmd_timer_install(hdl, NULL, NULL, cma.cma_cpu_curdelay);
31331e37bbvn}
31431e37bbvn
31531e37bbvnvoid
31631e37bbvncma_cpu_fini(fmd_hdl_t *hdl)
31731e37bbvn{
31831e37bbvn	cma_cpu_t *cpu;
31931e37bbvn
32031e37bbvn	while ((cpu = cma.cma_cpus) != NULL) {
32131e37bbvn		cma.cma_cpus = cpu->cpu_next;
32231e37bbvn		cma_cpu_free(hdl, cpu);
32331e37bbvn	}
32431e37bbvn}
325