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

📄 mscp.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *	@(#)mscp.c	7.5 (Berkeley) 12/16/90 *//* * MSCP generic driver routines */#include "sys/param.h"#include "sys/buf.h"#include "sys/errno.h"#include "sys/dkstat.h"#include "sys/ioctl.h"#include "sys/disklabel.h"#include "sys/syslog.h"#include "../uba/ubavar.h"#include "mscp.h"#include "mscpvar.h"#define	PCMD	PSWP		/* priority for command packet waits *//* * During transfers, mapping info is saved in the buffer's b_resid. */#define	b_info b_resid/* * Get a command packet.  Second argument is true iff we are * to wait if necessary.  Return NULL if none are available and * we cannot wait. */struct mscp *mscp_getcp(mi, canwait)	register struct mscp_info *mi;	int canwait;{#define	mri	(&mi->mi_cmd)	register struct mscp *mp;	register int i;	int s = spl5();again:	/*	 * Ensure that we have some command credits, and	 * that the next command packet is free.	 */	if (mi->mi_credits <= MSCP_MINCREDITS) {		if (!canwait) {			splx(s);			return (NULL);		}		mi->mi_wantcredits = 1;		sleep((caddr_t) &mi->mi_wantcredits, PCMD);		goto again;	}	i = mri->mri_next;	if (mri->mri_desc[i] & MSCP_OWN) {		if (!canwait) {			splx(s);			return (NULL);		}		mi->mi_wantcmd = 1;		sleep((caddr_t) &mi->mi_wantcmd, PCMD);		goto again;	}	mi->mi_credits--;	mri->mri_desc[i] &= ~MSCP_INT;	mri->mri_next = (mri->mri_next + 1) % mri->mri_size;	splx(s);	mp = &mri->mri_ring[i];	/*	 * Initialise some often-zero fields.	 * ARE THE LAST TWO NECESSARY IN GENERAL?  IT SURE WOULD BE	 * NICE IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS.	 */	mp->mscp_msglen = MSCP_MSGLEN;	mp->mscp_flags = 0;	mp->mscp_modifier = 0;	mp->mscp_seq.seq_bytecount = 0;	mp->mscp_seq.seq_buffer = 0;	mp->mscp_seq.seq_mapbase = 0;/*???*/	mp->mscp_sccc.sccc_errlgfl = 0;/*???*/	mp->mscp_sccc.sccc_copyspd = 0;	return (mp);#undef	mri}#ifdef AVOID_EMULEX_BUGint	mscp_aeb_xor = 0x8000bb80;#endif/* * Do a device go.  The driver calls this once it has allocated * resources for the transfer.  Save the resource information in * bp->b_ubinfo, and finish the MSCP packet. * * N.B.: If we were blocked for some time, the drive could have gone * off line and might still be that way.  We should probably handle * such a case by changing this command into an on line request and * not dequeuing the transfer after all. */mscp_go(mi, mp, info)	register struct mscp_info *mi;	register struct mscp *mp;	int info;{	register struct buf *bp, *dp;	/*	 * Now is also the time to move the transfer off the	 * controller and drive queues, and shuffle the drive	 * queue on the controller queue.  The idea is to try	 * to keep as many drives busy as possible---to deal	 * the controller's credits out to the drives in a `fair	 * share' arrangement.  (To do this fully would be more	 * trouble than it is worth, though.)	 */	dp = mi->mi_tab->b_actf;	bp = dp->b_actf;	dp->b_actf = bp->av_forw;	/* transfer off drive queue */	mi->mi_tab->b_actf = dp->b_forw;/* drive off ctlr queue */	APPEND(dp, mi->mi_tab, b_forw);	/* then back again */	/*	 * Move the buffer to the I/O wait queue.	 */	bp->av_back = mi->mi_wtab.av_back;	bp->av_forw = &mi->mi_wtab;	mi->mi_wtab.av_back->av_forw = bp;	mi->mi_wtab.av_back = bp;	/*	 * Save the mapping info, finish the command packet, and give	 * it to the device.  The device's dgo routine should then	 * initiate polling.	 */	bp->b_info = info;#ifdef AVOID_EMULEX_BUG	/*	 * The Emulex SC41/MS will occasionally zero the lower half word	 * of the command reference number.  The upper half word remains	 * intact.  To keep running, we convert the buffer address into	 * a small but nonzero integer that is unique over all pending	 * transfers, and store that value in the upper half word.  To	 * catch occurrances of the bug (so that we can gripe to Emulex),	 * we also put a nonzero value in the lower word.	 */	{		register u_int i = mi->mi_nextbp;		do {		/* find a free value */			if (mi->mi_bp[i] == 0)				goto found;			i = (i + 1) % AEB_MAX_BP;		} while (i != mi->mi_nextbp);		panic("mscp_go: AEB_MAX_BP too small");found:		mi->mi_bp[i++] = bp;		mi->mi_nextbp = i % AEB_MAX_BP;		mp->mscp_cmdref = (i << 16) ^ mscp_aeb_xor;	}#else	mp->mscp_cmdref = (long) bp;#endif	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;}/* * Handle a response ring transition. */mscp_dorsp(mi)	register struct mscp_info *mi;{	register struct uba_device *ui;	register struct buf *bp;	register struct mscp *mp;	register int nextrsp;	struct mscp_driver *md = mi->mi_md;	char *ctlrname, *drivename;	int st, error, info;	ctlrname = md->md_mname;	drivename = md->md_dname;	nextrsp = mi->mi_rsp.mri_next;loop:	if (mi->mi_rsp.mri_desc[nextrsp] & MSCP_OWN) {		/*		 * No more responses.  Remember the next expected		 * response index.  Check to see if we have some		 * credits back, and wake up sleepers if so.		 */		mi->mi_rsp.mri_next = nextrsp;		if (mi->mi_wantcredits && mi->mi_credits > MSCP_MINCREDITS) {			mi->mi_wantcredits = 0;			wakeup((caddr_t) &mi->mi_wantcredits);		}		return;	}	/*	 * Found a response.  Update credit information.  If there is	 * nothing else to do, jump to `done' to get the next response.	 */	mp = &mi->mi_rsp.mri_ring[nextrsp];	mi->mi_credits += MSCP_CREDITS(mp->mscp_msgtc);	switch (MSCP_MSGTYPE(mp->mscp_msgtc)) {	case MSCPT_SEQ:		break;	case MSCPT_DATAGRAM:		(*md->md_dgram)(mi, mp);		goto done;	case MSCPT_CREDITS:		goto done;	case MSCPT_MAINTENANCE:	default:		printf("%s%d: unit %d: unknown message type 0x%x ignored\n",			ctlrname, mi->mi_ctlr, mp->mscp_unit,			MSCP_MSGTYPE(mp->mscp_msgtc));		goto done;	}	/*	 * Controllers are allowed to interrupt as any drive, so we	 * must check the command before checking for a drive.	 */	if (mp->mscp_opcode == (M_OP_SETCTLRC | M_OP_END)) {		(*md->md_ctlrdone)(mi, mp);		goto done;	}	/*	 * Find the drive info.  If there is none, and this is an	 * available attention response, try configuring a new drive.	 */	if (mp->mscp_unit > md->md_ndpc) {		printf("%s%d: unit %d out of range\n",			ctlrname, mi->mi_ctlr, mp->mscp_unit);		goto done;	}	if ((ui = mi->mi_ip[mp->mscp_unit]) == NULL) {		if ((*md->md_unconf)(mi, mp) != MSCP_DONE) {			printf("%s%d: unit %d not configured, ",				ctlrname, mi->mi_ctlr, mp->mscp_unit);			if (mp->mscp_opcode == M_OP_AVAILATTN)				printf("available attn");			else				printf("stray response op 0x%x status 0x%x",					mp->mscp_opcode, mp->mscp_status);			printf(" ignored\n");		}		goto done;	}	/*	 * Handle individual responses.	 */	st = mp->mscp_status & M_ST_MASK;	error = 0;	switch (mp->mscp_opcode) {	case M_OP_END:		/*		 * The controller presents a bogus END packet when		 * a read/write command is given with an illegal		 * block number.  This is contrary to the MSCP		 * specification (ENDs are to be given only for		 * invalid commands), but that is the way of it.		 */		if (st == M_ST_INVALCMD && mp->mscp_cmdref != 0) {			printf("%s%d: bad lbn (%d)?\n", drivename,				ui->ui_unit, mp->mscp_seq.seq_lbn);			error = EIO;			goto rwend;		}		goto unknown;	case M_OP_ONLINE | M_OP_END:		/*		 * Finished an ON LINE request.  Call the driver to		 * find out whether it succeeded.  If so, mark it on		 * line.		 */		if (ui->ui_flags & UNIT_ONLINE) {			printf("%s%d: duplicate ONLINE ignored\n",				drivename, ui->ui_unit);			break;		}		if ((*md->md_online)(ui, mp) == MSCP_DONE)			ui->ui_flags |= UNIT_ONLINE;		break;	case M_OP_GETUNITST | M_OP_END:		/*		 * Got unit status.  Call the driver to find out		 * whether it succeeded, and if so, mark it.		 */		if ((*md->md_gotstatus)(ui, mp) == MSCP_DONE)			ui->ui_flags |= UNIT_HAVESTATUS;		break;	case M_OP_AVAILATTN:		/*		 * The drive went offline and we did not notice.		 * Mark it off line now, to force an on line request		 * next, so we can make sure it is still the same		 * drive.		 *		 * IF THE UDA DRIVER HAS A COMMAND AWAITING UNIBUS		 * RESOURCES, THAT COMMAND MAY GO OUT BEFORE THE ON		 * LINE.  IS IT WORTH FIXING??		 */		ui->ui_flags &= ~(UNIT_ONLINE | UNIT_HAVESTATUS);#ifdef notyet		(*md->md_offline)(ui, mp);#endif		break;	case M_OP_READ | M_OP_END:	case M_OP_WRITE | M_OP_END:		/*		 * A transfer finished.  Get the buffer, and release its		 * map registers via ubadone().  If the command finished		 * with an off line or available status, the drive went		 * off line (the idiot controller does not tell us until		 * it comes back *on* line, or until we try to use it).		 */		if (mp->mscp_cmdref == 0) {			/*			 * No buffer means there is a bug somewhere!			 */			printf("%s%d: io done, but no buffer?\n",				drivename, ui->ui_unit);			mscp_hexdump(mp);			break;		}rwend:#ifdef AVOID_EMULEX_BUG		{			register u_short *p = (u_short *) &mp->mscp_cmdref;			/*			 * Note any errors on the part of the controller.			 * The lower word should be zero after exclusive			 * or'ing with mscp_aeb_xor, and the upper should			 * then be in the range [1..AEB_MAX_BP].			 */			mp->mscp_cmdref ^= mscp_aeb_xor;			p[1]--;			if (p[1] >= AEB_MAX_BP)				panic("unrecoverable Emulex screwup");			if (p[0] == 0)				mi->mi_ok++;			else {				/*				 * Calculate the expected response,				 * assuming p[1] is correct.  The				 * actual response is then the expected				 * response xor p[0].				 */				int sb = ((p[1] + 1) << 16) ^ mscp_aeb_xor;				log(LOG_WARNING, "\Emulex SC41/MS screwup: %s%d, got %d correct, then changed 0x%x to 0x%x\n",					ctlrname, mi->mi_ctlr,					mi->mi_ok, sb, sb ^ p[0]);				mi->mi_ok = 0;			}			/* convert index back to buffer, and mark free */			bp = mi->mi_bp[p[1]];			mi->mi_bp[p[1]] = 0;		}#else		bp = (struct buf *) mp->mscp_cmdref;#ifdef MSCP_PARANOIA		{			register struct buf *q = mi->mi_wtab.av_forw;			/*			 * Ensure that this response corresponds to			 * some outstanding request.  If not, ignore			 * it entirely.  This will likely cause a			 * Unibus reset soon, after which the controller			 * just might behave.			 */			while (q != bp && q != &mi->mi_wtab)				q = q->av_forw;			if (q != bp) {				printf("%s%d: bad response packet ignored\n",					ctlrname, mi->mi_ctlr);				mscp_hexdump(mp);				goto out;			}		}#endif MSCP_PARANOIA#endif AVOID_EMULEX_BUG		/*		 * Mark any error-due-to-bad-LBN (via `goto rwend').		 * WHAT STATUS WILL THESE HAVE?  IT SURE WOULD BE NICE		 * IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS.		 */		if (error) {			bp->b_flags |= B_ERROR;			bp->b_error = error;		}		if (st == M_ST_OFFLINE || st == M_ST_AVAILABLE) {			ui->ui_flags &= ~(UNIT_ONLINE | UNIT_HAVESTATUS);#ifdef notyet			(*md->md_offline)(ui, mp);#endif		}		/*		 * Unlink the transfer from the wait queue mi_wtab.		 * If there are no more transfers on the drive queue		 * for this drive, and it is a profiled disk, turn		 * off its busy bit.		 */		bp->av_back->av_forw = bp->av_forw;

⌨️ 快捷键说明

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