eeh.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 750 行 · 第 1/2 页

C
750
字号
	if (!eeh_subsystem_enabled)		return 0;	if (!dn)		return 0;	/* Access to IO BARs might get this far and still not want checking. */	if (!(dn->eeh_mode & EEH_MODE_SUPPORTED) ||	    dn->eeh_mode & EEH_MODE_NOCHECK) {		return 0;	}	if (!dn->eeh_config_addr) {		return 0;	}	/*	 * Now test for an EEH failure.  This is VERY expensive.	 * Note that the eeh_config_addr may be a parent device	 * in the case of a device behind a bridge, or it may be	 * function zero of a multi-function device.	 * In any case they must share a common PHB.	 */	ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,			dn->eeh_config_addr, BUID_HI(dn->phb->buid),			BUID_LO(dn->phb->buid));	if (ret == 0 && rets[1] == 1 && (rets[0] == 2 || rets[0] == 4)) {		int log_event;		spin_lock_irqsave(&slot_errbuf_lock, flags);		memset(slot_errbuf, 0, eeh_error_buf_size);		log_event = rtas_call(ibm_slot_error_detail,		                      8, 1, NULL, dn->eeh_config_addr,		                      BUID_HI(dn->phb->buid),		                      BUID_LO(dn->phb->buid), NULL, 0,		                      virt_to_phys(slot_errbuf),		                      eeh_error_buf_size,		                      1 /* Temporary Error */);		if (log_event == 0)			log_error(slot_errbuf, ERR_TYPE_RTAS_LOG,				  1 /* Fatal */);		spin_unlock_irqrestore(&slot_errbuf_lock, flags);		printk(KERN_INFO "EEH: MMIO failure (%d) on device: %s %s\n",		       rets[0], dn->name, dn->full_name);		WARN_ON(1);		/*		 * XXX We should create a separate sysctl for this.		 *		 * Since the panic_on_oops sysctl is used to halt		 * the system in light of potential corruption, we		 * can use it here.		 */		if (panic_on_oops) {			panic("EEH: MMIO failure (%d) on device: %s %s\n",			      rets[0], dn->name, dn->full_name);		} else {			__get_cpu_var(ignored_failures)++;		}	} else {		__get_cpu_var(false_positives)++;	}	return 0;}EXPORT_SYMBOL(eeh_dn_check_failure);/** * eeh_check_failure - check if all 1's data is due to EEH slot freeze * @token i/o token, should be address in the form 0xA.... * @val value, should be all 1's (XXX why do we need this arg??) * * Check for an eeh failure at the given token address. * Check for an EEH failure at the given token address.  Call this * routine if the result of a read was all 0xff's and you want to * find out if this is due to an EEH slot freeze event.  This routine * will query firmware for the EEH status. * * Note this routine is safe to call in an interrupt context. */unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val){	unsigned long addr;	struct pci_dev *dev;	struct device_node *dn;	/* Finding the phys addr + pci device; this is pretty quick. */	addr = eeh_token_to_phys((unsigned long __force) token);	dev = pci_get_device_by_addr(addr);	if (!dev)		return val;	dn = pci_device_to_OF_node(dev);	eeh_dn_check_failure (dn, dev);	pci_dev_put(dev);	return val;}EXPORT_SYMBOL(eeh_check_failure);struct eeh_early_enable_info {	unsigned int buid_hi;	unsigned int buid_lo;};/* Enable eeh for the given device node. */static void *early_enable_eeh(struct device_node *dn, void *data){	struct eeh_early_enable_info *info = data;	int ret;	char *status = get_property(dn, "status", NULL);	u32 *class_code = (u32 *)get_property(dn, "class-code", NULL);	u32 *vendor_id = (u32 *)get_property(dn, "vendor-id", NULL);	u32 *device_id = (u32 *)get_property(dn, "device-id", NULL);	u32 *regs;	int enable;	dn->eeh_mode = 0;	if (status && strcmp(status, "ok") != 0)		return NULL;	/* ignore devices with bad status */	/* Ignore bad nodes. */	if (!class_code || !vendor_id || !device_id)		return NULL;	/* There is nothing to check on PCI to ISA bridges */	if (dn->type && !strcmp(dn->type, "isa")) {		dn->eeh_mode |= EEH_MODE_NOCHECK;		return NULL;	}	/*	 * Now decide if we are going to "Disable" EEH checking	 * for this device.  We still run with the EEH hardware active,	 * but we won't be checking for ff's.  This means a driver	 * could return bad data (very bad!), an interrupt handler could	 * hang waiting on status bits that won't change, etc.	 * But there are a few cases like display devices that make sense.	 */	enable = 1;	/* i.e. we will do checking */	if ((*class_code >> 16) == PCI_BASE_CLASS_DISPLAY)		enable = 0;	if (!enable)		dn->eeh_mode |= EEH_MODE_NOCHECK;	/* Ok... see if this device supports EEH.  Some do, some don't,	 * and the only way to find out is to check each and every one. */	regs = (u32 *)get_property(dn, "reg", NULL);	if (regs) {		/* First register entry is addr (00BBSS00)  */		/* Try to enable eeh */		ret = rtas_call(ibm_set_eeh_option, 4, 1, NULL,				regs[0], info->buid_hi, info->buid_lo,				EEH_ENABLE);		if (ret == 0) {			eeh_subsystem_enabled = 1;			dn->eeh_mode |= EEH_MODE_SUPPORTED;			dn->eeh_config_addr = regs[0];#ifdef DEBUG			printk(KERN_DEBUG "EEH: %s: eeh enabled\n", dn->full_name);#endif		} else {			/* This device doesn't support EEH, but it may have an			 * EEH parent, in which case we mark it as supported. */			if (dn->parent && (dn->parent->eeh_mode & EEH_MODE_SUPPORTED)) {				/* Parent supports EEH. */				dn->eeh_mode |= EEH_MODE_SUPPORTED;				dn->eeh_config_addr = dn->parent->eeh_config_addr;				return NULL;			}		}	} else {		printk(KERN_WARNING "EEH: %s: unable to get reg property.\n",		       dn->full_name);	}	return NULL; }/* * Initialize EEH by trying to enable it for all of the adapters in the system. * As a side effect we can determine here if eeh is supported at all. * Note that we leave EEH on so failed config cycles won't cause a machine * check.  If a user turns off EEH for a particular adapter they are really * telling Linux to ignore errors.  Some hardware (e.g. POWER5) won't * grant access to a slot if EEH isn't enabled, and so we always enable * EEH for all slots/all devices. * * The eeh-force-off option disables EEH checking globally, for all slots. * Even if force-off is set, the EEH hardware is still enabled, so that * newer systems can boot. */void __init eeh_init(void){	struct device_node *phb, *np;	struct eeh_early_enable_info info;	init_pci_config_tokens();	np = of_find_node_by_path("/rtas");	if (np == NULL)		return;	ibm_set_eeh_option = rtas_token("ibm,set-eeh-option");	ibm_set_slot_reset = rtas_token("ibm,set-slot-reset");	ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state");	ibm_slot_error_detail = rtas_token("ibm,slot-error-detail");	if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE)		return;	eeh_error_buf_size = rtas_token("rtas-error-log-max");	if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) {		eeh_error_buf_size = 1024;	}	if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) {		printk(KERN_WARNING "EEH: rtas-error-log-max is bigger than allocated "		      "buffer ! (%d vs %d)", eeh_error_buf_size, RTAS_ERROR_LOG_MAX);		eeh_error_buf_size = RTAS_ERROR_LOG_MAX;	}	/* Enable EEH for all adapters.  Note that eeh requires buid's */	for (phb = of_find_node_by_name(NULL, "pci"); phb;	     phb = of_find_node_by_name(phb, "pci")) {		unsigned long buid;		buid = get_phb_buid(phb);		if (buid == 0)			continue;		info.buid_lo = BUID_LO(buid);		info.buid_hi = BUID_HI(buid);		traverse_pci_devices(phb, early_enable_eeh, &info);	}	if (eeh_subsystem_enabled)		printk(KERN_INFO "EEH: PCI Enhanced I/O Error Handling Enabled\n");	else		printk(KERN_WARNING "EEH: No capable adapters found\n");}/** * eeh_add_device_early - enable EEH for the indicated device_node * @dn: device node for which to set up EEH * * This routine must be used to perform EEH initialization for PCI * devices that were added after system boot (e.g. hotplug, dlpar). * This routine must be called before any i/o is performed to the * adapter (inluding any config-space i/o). * Whether this actually enables EEH or not for this device depends * on the CEC architecture, type of the device, on earlier boot * command-line arguments & etc. */void eeh_add_device_early(struct device_node *dn){	struct pci_controller *phb;	struct eeh_early_enable_info info;	if (!dn || !eeh_subsystem_enabled)		return;	phb = dn->phb;	if (NULL == phb || 0 == phb->buid) {		printk(KERN_WARNING "EEH: Expected buid but found none\n");		return;	}	info.buid_hi = BUID_HI(phb->buid);	info.buid_lo = BUID_LO(phb->buid);	early_enable_eeh(dn, &info);}EXPORT_SYMBOL(eeh_add_device_early);/** * eeh_add_device_late - perform EEH initialization for the indicated pci device * @dev: pci device for which to set up EEH * * This routine must be used to complete EEH initialization for PCI * devices that were added after system boot (e.g. hotplug, dlpar). */void eeh_add_device_late(struct pci_dev *dev){	if (!dev || !eeh_subsystem_enabled)		return;#ifdef DEBUG	printk(KERN_DEBUG "EEH: adding device %s %s\n", pci_name(dev),	       pci_pretty_name(dev));#endif	pci_addr_cache_insert_device (dev);}EXPORT_SYMBOL(eeh_add_device_late);/** * eeh_remove_device - undo EEH setup for the indicated pci device * @dev: pci device to be removed * * This routine should be when a device is removed from a running * system (e.g. by hotplug or dlpar). */void eeh_remove_device(struct pci_dev *dev){	if (!dev || !eeh_subsystem_enabled)		return;	/* Unregister the device with the EEH/PCI address search system */#ifdef DEBUG	printk(KERN_DEBUG "EEH: remove device %s %s\n", pci_name(dev),	       pci_pretty_name(dev));#endif	pci_addr_cache_remove_device(dev);}EXPORT_SYMBOL(eeh_remove_device);static int proc_eeh_show(struct seq_file *m, void *v){	unsigned int cpu;	unsigned long ffs = 0, positives = 0, failures = 0;	for_each_cpu(cpu) {		ffs += per_cpu(total_mmio_ffs, cpu);		positives += per_cpu(false_positives, cpu);		failures += per_cpu(ignored_failures, cpu);	}	if (0 == eeh_subsystem_enabled) {		seq_printf(m, "EEH Subsystem is globally disabled\n");		seq_printf(m, "eeh_total_mmio_ffs=%ld\n", ffs);	} else {		seq_printf(m, "EEH Subsystem is enabled\n");		seq_printf(m, "eeh_total_mmio_ffs=%ld\n"			   "eeh_false_positives=%ld\n"			   "eeh_ignored_failures=%ld\n",			   ffs, positives, failures);	}	return 0;}static int proc_eeh_open(struct inode *inode, struct file *file){	return single_open(file, proc_eeh_show, NULL);}static struct file_operations proc_eeh_operations = {	.open      = proc_eeh_open,	.read      = seq_read,	.llseek    = seq_lseek,	.release   = single_release,};static int __init eeh_init_proc(void){	struct proc_dir_entry *e;	if (systemcfg->platform & PLATFORM_PSERIES) {		e = create_proc_entry("ppc64/eeh", 0, NULL);		if (e)			e->proc_fops = &proc_eeh_operations;	}	return 0;}__initcall(eeh_init_proc);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?