📄 mscp.c
字号:
/* * 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 + -