⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chsc.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  drivers/s390/cio/chsc.c *   S/390 common I/O routines -- channel subsystem call *   $Revision: 1.115 $ * *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, *			      IBM Corporation *    Author(s): Ingo Adlung (adlung@de.ibm.com) *		 Cornelia Huck (cohuck@de.ibm.com) *		 Arnd Bergmann (arndb@de.ibm.com) */#include <linux/module.h>#include <linux/config.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/device.h>#include <asm/cio.h>#include "css.h"#include "cio.h"#include "cio_debug.h"#include "ioasm.h"#include "chsc.h"static struct channel_path *chps[NR_CHPIDS];static void *sei_page;static int new_channel_path(int chpid);static inline voidset_chp_logically_online(int chp, int onoff){	chps[chp]->state = onoff;}static intget_chp_status(int chp){	return (chps[chp] ? chps[chp]->state : -ENODEV);}voidchsc_validate_chpids(struct subchannel *sch){	int mask, chp;	for (chp = 0; chp <= 7; chp++) {		mask = 0x80 >> chp;		if (!get_chp_status(sch->schib.pmcw.chpid[chp]))			/* disable using this path */			sch->opm &= ~mask;	}}voidchpid_is_actually_online(int chp){	int state;	state = get_chp_status(chp);	if (state < 0) {		need_rescan = 1;		queue_work(slow_path_wq, &slow_path_work);	} else		WARN_ON(!state);}/* FIXME: this is _always_ called for every subchannel. shouldn't we *	  process more than one at a time? */static intchsc_get_sch_desc_irq(struct subchannel *sch, void *page){	int ccode, j;	struct {		struct chsc_header request;		u16 reserved1;		u16 f_sch;	  /* first subchannel */		u16 reserved2;		u16 l_sch;	  /* last subchannel */		u32 reserved3;		struct chsc_header response;		u32 reserved4;		u8 sch_valid : 1;		u8 dev_valid : 1;		u8 st	     : 3; /* subchannel type */		u8 zeroes    : 3;		u8  unit_addr;	  /* unit address */		u16 devno;	  /* device number */		u8 path_mask;		u8 fla_valid_mask;		u16 sch;	  /* subchannel */		u8 chpid[8];	  /* chpids 0-7 */		u16 fla[8];	  /* full link addresses 0-7 */	} *ssd_area;	ssd_area = page;	ssd_area->request = (struct chsc_header) {		.length = 0x0010,		.code   = 0x0004,	};	ssd_area->f_sch = sch->irq;	ssd_area->l_sch = sch->irq;	ccode = chsc(ssd_area);	if (ccode > 0) {		pr_debug("chsc returned with ccode = %d\n", ccode);		return (ccode == 3) ? -ENODEV : -EBUSY;	}	switch (ssd_area->response.code) {	case 0x0001: /* everything ok */		break;	case 0x0002:		CIO_CRW_EVENT(2, "Invalid command!\n");		return -EINVAL;	case 0x0003:		CIO_CRW_EVENT(2, "Error in chsc request block!\n");		return -EINVAL;	case 0x0004:		CIO_CRW_EVENT(2, "Model does not provide ssd\n");		return -EOPNOTSUPP;	default:		CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",			      ssd_area->response.code);		return -EIO;	}	/*	 * ssd_area->st stores the type of the detected	 * subchannel, with the following definitions:	 *	 * 0: I/O subchannel:	  All fields have meaning	 * 1: CHSC subchannel:	  Only sch_val, st and sch	 *			  have meaning	 * 2: Message subchannel: All fields except unit_addr	 *			  have meaning	 * 3: ADM subchannel:	  Only sch_val, st and sch	 *			  have meaning	 *	 * Other types are currently undefined.	 */	if (ssd_area->st > 3) { /* uhm, that looks strange... */		CIO_CRW_EVENT(0, "Strange subchannel type %d"			      " for sch %04x\n", ssd_area->st, sch->irq);		/*		 * There may have been a new subchannel type defined in the		 * time since this code was written; since we don't know which		 * fields have meaning and what to do with it we just jump out		 */		return 0;	} else {		const char *type[4] = {"I/O", "chsc", "message", "ADM"};		CIO_CRW_EVENT(6, "ssd: sch %04x is %s subchannel\n",			      sch->irq, type[ssd_area->st]);		sch->ssd_info.valid = 1;		sch->ssd_info.type = ssd_area->st;	}	if (ssd_area->st == 0 || ssd_area->st == 2) {		for (j = 0; j < 8; j++) {			if (!((0x80 >> j) & ssd_area->path_mask &			      ssd_area->fla_valid_mask))				continue;			sch->ssd_info.chpid[j] = ssd_area->chpid[j];			sch->ssd_info.fla[j]   = ssd_area->fla[j];		}	}	return 0;}intcss_get_ssd_info(struct subchannel *sch){	int ret;	void *page;	page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);	if (!page)		return -ENOMEM;	spin_lock_irq(&sch->lock);	ret = chsc_get_sch_desc_irq(sch, page);	if (ret) {		static int cio_chsc_err_msg;				if (!cio_chsc_err_msg) {			printk(KERN_ERR			       "chsc_get_sch_descriptions:"			       " Error %d while doing chsc; "			       "processing some machine checks may "			       "not work\n", ret);			cio_chsc_err_msg = 1;		}	}	spin_unlock_irq(&sch->lock);	free_page((unsigned long)page);	if (!ret) {		int j, chpid;		/* Allocate channel path structures, if needed. */		for (j = 0; j < 8; j++) {			chpid = sch->ssd_info.chpid[j];			if (chpid && (get_chp_status(chpid) < 0))			    new_channel_path(chpid);		}	}	return ret;}static ints390_subchannel_remove_chpid(struct device *dev, void *data){	int j;	int mask;	struct subchannel *sch;	__u8 *chpid;	struct schib schib;	sch = to_subchannel(dev);	chpid = data;	for (j = 0; j < 8; j++)		if (sch->schib.pmcw.chpid[j] == *chpid)			break;	if (j >= 8)		return 0;	mask = 0x80 >> j;	spin_lock(&sch->lock);	stsch(sch->irq, &schib);	if (!schib.pmcw.dnv)		goto out_unreg;	memcpy(&sch->schib, &schib, sizeof(struct schib));	/* Check for single path devices. */	if (sch->schib.pmcw.pim == 0x80)		goto out_unreg;	if (sch->vpm == mask)		goto out_unreg;	if ((sch->schib.scsw.actl & (SCSW_ACTL_CLEAR_PEND |				     SCSW_ACTL_HALT_PEND |				     SCSW_ACTL_START_PEND |				     SCSW_ACTL_RESUME_PEND)) &&	    (sch->schib.pmcw.lpum == mask)) {		int cc = cio_cancel(sch);				if (cc == -ENODEV)			goto out_unreg;		if (cc == -EINVAL) {			cc = cio_clear(sch);			if (cc == -ENODEV)				goto out_unreg;			/* Call handler. */			if (sch->driver && sch->driver->termination)				sch->driver->termination(&sch->dev);			goto out_unlock;		}	} else if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&		   (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&		   (sch->schib.pmcw.lpum == mask)) {		int cc;		cc = cio_clear(sch);		if (cc == -ENODEV)			goto out_unreg;		/* Call handler. */		if (sch->driver && sch->driver->termination)			sch->driver->termination(&sch->dev);		goto out_unlock;	}	/* trigger path verification. */	if (sch->driver && sch->driver->verify)		sch->driver->verify(&sch->dev);out_unlock:	spin_unlock(&sch->lock);	return 0;out_unreg:	spin_unlock(&sch->lock);	sch->lpm = 0;	if (css_enqueue_subchannel_slow(sch->irq)) {		css_clear_subchannel_slow_list();		need_rescan = 1;	}	return 0;}static inline voids390_set_chpid_offline( __u8 chpid){	char dbf_txt[15];	sprintf(dbf_txt, "chpr%x", chpid);	CIO_TRACE_EVENT(2, dbf_txt);	if (get_chp_status(chpid) <= 0)		return;	bus_for_each_dev(&css_bus_type, NULL, &chpid,			 s390_subchannel_remove_chpid);	if (need_rescan || css_slow_subchannels_exist())		queue_work(slow_path_wq, &slow_path_work);}static ints390_process_res_acc_sch(u8 chpid, __u16 fla, u32 fla_mask,			 struct subchannel *sch){	int found;	int chp;	int ccode;		found = 0;	for (chp = 0; chp <= 7; chp++)		/*		 * check if chpid is in information updated by ssd		 */		if (sch->ssd_info.valid &&		    sch->ssd_info.chpid[chp] == chpid &&		    (sch->ssd_info.fla[chp] & fla_mask) == fla) {			found = 1;			break;		}		if (found == 0)		return 0;	/*	 * Do a stsch to update our subchannel structure with the	 * new path information and eventually check for logically	 * offline chpids.	 */	ccode = stsch(sch->irq, &sch->schib);	if (ccode > 0)		return 0;	return 0x80 >> chp;}static ints390_process_res_acc (u8 chpid, __u16 fla, u32 fla_mask){	struct subchannel *sch;	int irq, rc;	char dbf_txt[15];	sprintf(dbf_txt, "accpr%x", chpid);	CIO_TRACE_EVENT( 2, dbf_txt);	if (fla != 0) {		sprintf(dbf_txt, "fla%x", fla);		CIO_TRACE_EVENT( 2, dbf_txt);	}	/*	 * I/O resources may have become accessible.	 * Scan through all subchannels that may be concerned and	 * do a validation on those.	 * The more information we have (info), the less scanning	 * will we have to do.	 */	if (!get_chp_status(chpid))		return 0; /* no need to do the rest */	rc = 0;	for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {		int chp_mask, old_lpm;		sch = get_subchannel_by_schid(irq);		if (!sch) {			struct schib schib;			int ret;			/*			 * We don't know the device yet, but since a path			 * may be available now to the device we'll have			 * to do recognition again.			 * Since we don't have any idea about which chpid			 * that beast may be on we'll have to do a stsch			 * on all devices, grr...			 */			if (stsch(irq, &schib)) {				/* We're through */				if (need_rescan)					rc = -EAGAIN;				break;			}			if (need_rescan) {				rc = -EAGAIN;				continue;			}			/* Put it on the slow path. */			ret = css_enqueue_subchannel_slow(irq);			if (ret) {				css_clear_subchannel_slow_list();				need_rescan = 1;			}			rc = -EAGAIN;			continue;		}			spin_lock_irq(&sch->lock);		chp_mask = s390_process_res_acc_sch(chpid, fla, fla_mask, sch);		if (chp_mask == 0) {			spin_unlock_irq(&sch->lock);			if (fla_mask != 0)				break;			else				continue;		}		old_lpm = sch->lpm;		sch->lpm = ((sch->schib.pmcw.pim &			     sch->schib.pmcw.pam &			     sch->schib.pmcw.pom)			    | chp_mask) & sch->opm;		spin_unlock_irq(&sch->lock);		if (!old_lpm && sch->lpm)			device_trigger_reprobe(sch);		else if (sch->driver && sch->driver->verify)			sch->driver->verify(&sch->dev);		put_device(&sch->dev);		if (fla_mask != 0)			break;	}	return rc;}static int__get_chpid_from_lir(void *data){	struct lir {		u8  iq;		u8  ic;		u16 sci;		/* incident-node descriptor */		u32 indesc[28];		/* attached-node descriptor */		u32 andesc[28];		/* incident-specific information */		u32 isinfo[28];	} *lir;	lir = (struct lir*) data;	if (!(lir->iq&0x80))		/* NULL link incident record */		return -EINVAL;	if (!(lir->indesc[0]&0xc0000000))		/* node descriptor not valid */		return -EINVAL;	if (!(lir->indesc[0]&0x10000000))		/* don't handle device-type nodes - FIXME */		return -EINVAL;	/* Byte 3 contains the chpid. Could also be CTCA, but we don't care */	return (u16) (lir->indesc[0]&0x000000ff);}intchsc_process_crw(void){	int chpid, ret;	struct {		struct chsc_header request;		u32 reserved1;		u32 reserved2;		u32 reserved3;		struct chsc_header response;		u32 reserved4;		u8  flags;		u8  vf;		/* validity flags */		u8  rs;		/* reporting source */		u8  cc;		/* content code */		u16 fla;	/* full link address */		u16 rsid;	/* reporting source id */		u32 reserved5;		u32 reserved6;		u32 ccdf[96];	/* content-code dependent field */		/* ccdf has to be big enough for a link-incident record */	} *sei_area;	if (!sei_page)		return 0;	/*	 * build the chsc request block for store event information	 * and do the call	 * This function is only called by the machine check handler thread,	 * so we don't need locking for the sei_page.	 */	sei_area = sei_page;	CIO_TRACE_EVENT( 2, "prcss");

⌨️ 快捷键说明

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