mirror of https://gitee.com/openkylin/linux.git
powerpc/eeh: Search PE based on requirement
The patch implements searching PE based on the following requirements: * Search PE according to PE address, which is traditional PE address that is composed of PCI bus/device/function number, or unified PE address assigned by firmware or platform. * Search parent PE according to the given EEH device. It's useful when creating new PE and put it into right position. Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
parent
55037d1761
commit
22f4ab123f
|
@ -164,6 +164,7 @@ static inline void eeh_unlock(void)
|
|||
*/
|
||||
#define EEH_MAX_ALLOWED_FREEZES 5
|
||||
|
||||
typedef void *(*eeh_traverse_func)(void *data, void *flag);
|
||||
int __devinit eeh_phb_pe_create(struct pci_controller *phb);
|
||||
|
||||
void * __devinit eeh_dev_init(struct device_node *dn, void *data);
|
||||
|
|
|
@ -118,3 +118,146 @@ static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* eeh_pe_next - Retrieve the next PE in the tree
|
||||
* @pe: current PE
|
||||
* @root: root PE
|
||||
*
|
||||
* The function is used to retrieve the next PE in the
|
||||
* hierarchy PE tree.
|
||||
*/
|
||||
static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
|
||||
struct eeh_pe *root)
|
||||
{
|
||||
struct list_head *next = pe->child_list.next;
|
||||
|
||||
if (next == &pe->child_list) {
|
||||
while (1) {
|
||||
if (pe == root)
|
||||
return NULL;
|
||||
next = pe->child.next;
|
||||
if (next != &pe->parent->child_list)
|
||||
break;
|
||||
pe = pe->parent;
|
||||
}
|
||||
}
|
||||
|
||||
return list_entry(next, struct eeh_pe, child);
|
||||
}
|
||||
|
||||
/**
|
||||
* eeh_pe_traverse - Traverse PEs in the specified PHB
|
||||
* @root: root PE
|
||||
* @fn: callback
|
||||
* @flag: extra parameter to callback
|
||||
*
|
||||
* The function is used to traverse the specified PE and its
|
||||
* child PEs. The traversing is to be terminated once the
|
||||
* callback returns something other than NULL, or no more PEs
|
||||
* to be traversed.
|
||||
*/
|
||||
static void *eeh_pe_traverse(struct eeh_pe *root,
|
||||
eeh_traverse_func fn, void *flag)
|
||||
{
|
||||
struct eeh_pe *pe;
|
||||
void *ret;
|
||||
|
||||
for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
|
||||
ret = fn(pe, flag);
|
||||
if (ret) return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* __eeh_pe_get - Check the PE address
|
||||
* @data: EEH PE
|
||||
* @flag: EEH device
|
||||
*
|
||||
* For one particular PE, it can be identified by PE address
|
||||
* or tranditional BDF address. BDF address is composed of
|
||||
* Bus/Device/Function number. The extra data referred by flag
|
||||
* indicates which type of address should be used.
|
||||
*/
|
||||
static void *__eeh_pe_get(void *data, void *flag)
|
||||
{
|
||||
struct eeh_pe *pe = (struct eeh_pe *)data;
|
||||
struct eeh_dev *edev = (struct eeh_dev *)flag;
|
||||
|
||||
/* Unexpected PHB PE */
|
||||
if (pe->type == EEH_PE_PHB)
|
||||
return NULL;
|
||||
|
||||
/* We prefer PE address */
|
||||
if (edev->pe_config_addr &&
|
||||
(edev->pe_config_addr == pe->addr))
|
||||
return pe;
|
||||
|
||||
/* Try BDF address */
|
||||
if (edev->pe_config_addr &&
|
||||
(edev->config_addr == pe->config_addr))
|
||||
return pe;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* eeh_pe_get - Search PE based on the given address
|
||||
* @edev: EEH device
|
||||
*
|
||||
* Search the corresponding PE based on the specified address which
|
||||
* is included in the eeh device. The function is used to check if
|
||||
* the associated PE has been created against the PE address. It's
|
||||
* notable that the PE address has 2 format: traditional PE address
|
||||
* which is composed of PCI bus/device/function number, or unified
|
||||
* PE address.
|
||||
*/
|
||||
static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
|
||||
{
|
||||
struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
|
||||
struct eeh_pe *pe;
|
||||
|
||||
eeh_lock();
|
||||
pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
|
||||
eeh_unlock();
|
||||
|
||||
return pe;
|
||||
}
|
||||
|
||||
/**
|
||||
* eeh_pe_get_parent - Retrieve the parent PE
|
||||
* @edev: EEH device
|
||||
*
|
||||
* The whole PEs existing in the system are organized as hierarchy
|
||||
* tree. The function is used to retrieve the parent PE according
|
||||
* to the parent EEH device.
|
||||
*/
|
||||
static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
|
||||
{
|
||||
struct device_node *dn;
|
||||
struct eeh_dev *parent;
|
||||
|
||||
/*
|
||||
* It might have the case for the indirect parent
|
||||
* EEH device already having associated PE, but
|
||||
* the direct parent EEH device doesn't have yet.
|
||||
*/
|
||||
dn = edev->dn->parent;
|
||||
while (dn) {
|
||||
/* We're poking out of PCI territory */
|
||||
if (!PCI_DN(dn)) return NULL;
|
||||
|
||||
parent = of_node_to_eeh_dev(dn);
|
||||
/* We're poking out of PCI territory */
|
||||
if (!parent) return NULL;
|
||||
|
||||
if (parent->pe)
|
||||
return parent->pe;
|
||||
|
||||
dn = dn->parent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue