/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/dditypes.h>
#include <sys/pcie_impl.h>
#include <sys/pathname.h>
/*
* The below 2 global variables are for PCIe IOV Error Handling. They must only
* be accessed during error handling under the protection of a error mutex.
*/
static void pcie_faulty_list_clear();
pcie_domains_t **headp);
{
int bus_num;
while (dip) {
return (dip);
if (bus_p) {
}
}
return (NULL);
}
/*
* Add a device bdf to the bdf list.
*/
static void
{
}
/*
* Remove a bdf from the bdf list.
*/
static void
{
return;
}
break;
} else
}
}
/*
* Cache IOV domain info in all it's parent's pcie_domain_t
*
* The leaf devices's domain info must be set before calling this function.
*/
void
{
if (assigned) {
if (domain_id)
if (fma_dom)
pdom_p->fmadom_count++;
else {
pdom_p->nfmadom_count++;
}
} else
pdom_p->rootdom_count++;
}
}
/*
* Clear the leaf device's domain info and uncache IOV domain info in all it's
* parent's pcie_domain_t
*
* The leaf devices's domain info is also cleared by calling this function.
*/
void
{
/* Clear the domain information */
if (domain_id) {
}
dom_p->fmadom_count = 0;
dom_p->nfmadom_count = 0;
dom_p->rootdom_count = 0;
if (assigned) {
if (domain_id)
if (fma_dom)
pdom_p->fmadom_count--;
else {
pdom_p->nfmadom_count--;
}
} else
pdom_p->rootdom_count--;
}
}
/*
* Initialize private data structure for IOV environments.
* o Allocate memory for iov data
* o Cache Domain ids.
*/
void
{
/* Only leaf devices are assignable to IO Domains */
if (PCIE_IS_BDG(bus_p))
return;
/*
* At the time of init_dom in the root domain a device may or may not
* have been assigned to an IO Domain.
*
* LDOMS: the property "ddi-assigned" will be set for devices that is
* assignable to an IO domain and unusable in the root domain. If the
* property exist assume it has been assigned to a non-fma domain until
* otherwise notified. The domain id is unknown on LDOMS.
*
* Xen: the "ddi-assigned" property won't be set until Xen store calls
* pcie_loan_device is called. In this function this will always look
* like the device is assigned to the root domain. Domain ID caching
* will occur in pcie_loan_device function.
*/
"ddi-assigned", -1) != -1) {
/* Prevent "assigned" device from detaching */
} else
}
void
{
if (PCIE_IS_BDG(bus_p))
else
}
/*
* PCIe Severity:
*
* PF_ERR_NO_ERROR : no IOV Action
* PF_ERR_CE : no IOV Action
* PF_ERR_NO_PANIC : contains error telemetry, log domain info
* PF_ERR_MATCHED_DEVICE: contains error telemetry, log domain info
* PF_ERR_MATCHED_RC : Error already taken care of, no further IOV Action
* PF_ERR_MATCHED_PARENT: Error already taken care of, no further IOV Action
* PF_ERR_PANIC : contains error telemetry, log domain info
*
* For NO_PANIC, MATCHED_DEVICE and PANIC, IOV wants to look at the affected
* devices and find the domains involved.
*
* If root domain does not own an affected device, IOV EH should change
* PF_ERR_PANIC to PF_ERR_MATCH_DOM.
*/
int
{
/*
* check if all devices under the root device are unassigned.
* this function should quickly return in non-IOV environment.
*/
return (severity);
if (severity & PF_ERR_PANIC_DEADLOCK) {
/* adjust affected flags to leverage cached domain ids */
if (dev_affected_flags & PF_AFFECTED_CHILDREN) {
}
for (affected_flag = 1;
affected_flag <<= 1) {
continue;
/*
* If a leaf device is assigned to the root domain or if
* a bridge has children assigned to a root domain
* panic.
*
* If a leaf device or a child of a bridge is assigned
* to NFMA domain mark it for panic. If assigned to FMA
* domain save the domain id.
*/
if (!PCIE_IS_BDG(a_bus_p) &&
!PCIE_IS_ASSIGNED(a_bus_p)) {
if (severity & PF_ERR_FATAL_FLAGS)
is_panic++;
continue;
}
if (severity & PF_ERR_FATAL_FLAGS)
is_panic++;
}
if ((PCIE_ASSIGNED_TO_NFMA_DOM(a_bus_p) ||
(severity & PF_ERR_FATAL_FLAGS)) {
}
if (PCIE_ASSIGNED_TO_FMA_DOM(a_bus_p)) {
}
if (PCIE_BDG_HAS_CHILDREN_FMA_DOM(a_bus_p)) {
}
}
/*
* Overwrite the severity only if affected device can be
* identified and root domain does not need to panic.
*/
if ((!is_panic) && is_aff_dev_found) {
}
}
return (iov_severity);
}
/* ARGSUSED */
void
{
/*
* check if all devices under the root device are unassigned.
* this function should quickly return in non-IOV environment.
*/
return;
}
{
if (!flag)
return (NULL);
switch (flag) {
case PF_AFFECTED_ROOT:
case PF_AFFECTED_SELF:
return (bus_p);
case PF_AFFECTED_PARENT:
case PF_AFFECTED_BDF: /* may only be used for RC */
if (!PCIE_CHECK_VALID_BDF(a_bdf))
return (NULL);
return (temp_bus_p);
case PF_AFFECTED_AER:
return (temp_bus_p);
}
break;
case PF_AFFECTED_SAER:
return (temp_bus_p);
}
break;
case PF_AFFECTED_ADDR: /* ROOT only */
return (temp_bus_p);
}
return (NULL);
}
/* type used for pcie_domain_list_find() function */
typedef enum {
/*
* Check if a domain id is already in the linked list
*/
static pcie_domains_t *
{
while (pd_list_p) {
return (pd_list_p);
if (type == PCIE_DOM_LIST_TYPE_CACHE) {
} else if (type == PCIE_DOM_LIST_TYPE_FAULT) {
} else {
return (NULL);
}
}
return (NULL);
}
/*
* Return true if a leaf device is assigned to a domain or a bridge device
* has children assigned to the domain
*/
{
if (PCIE_IS_BDG(bus_p)) {
return (B_TRUE);
return (B_FALSE);
} else {
}
}
/*
* Add a domain id to a cached domain id list.
* If the domain already exists in the list, increment the reference count.
*/
void
{
} else
pd->cached_count++;
}
/*
* Remove a domain id from a cached domain id list.
* Decrement the reference count.
*/
void
{
if (pd) {
}
}
/* destroy cached domain id list */
static void
{
pcie_domains_t *p = domain_ids;
while (p) {
next = p->cached_next;
kmem_free(p, sizeof (pcie_domains_t));
p = next;
}
}
static void
{
return;
} else {
}
}
static void
{
/* unlink all domain structures from the faulty list */
while (pd) {
pd->faulty_count = 0;
}
}
void
{
if (pcie_faulty_all)
return;
if (domain_ids == NULL)
return;
if (!new_list_p->cached_count)
continue;
/* search domain id in the faulty domain list */
if (pd)
pd->faulty_count++;
else
}
}