vinumrequest.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 998 行 · 第 1/3 页

C
998
字号
/*- * Copyright (c) 1997, 1998 *	Nan Yang Computer Services Limited.  All rights reserved. * *  This software is distributed under the so-called ``Berkeley *  License'': * * 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 Nan Yang Computer *      Services Limited. * 4. Neither the name of the Company 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 ``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 company 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. * * $Id: vinumrequest.c,v 1.7.2.4 1999/04/06 09:05:58 grog Exp $ */#define REALLYKERNEL#include "opt_vinum.h"#include <dev/vinum/vinumhdr.h>#include <dev/vinum/request.h>#include <miscfs/specfs/specdev.h>#include <sys/resourcevar.h>enum requeststatus bre(struct request *rq,    int plexno,    daddr_t * diskstart,    daddr_t diskend);enum requeststatus bre5(struct request *rq,    int plexno,    daddr_t * diskstart,    daddr_t diskend);enum requeststatus build_read_request(struct request *rq, int volplexno);enum requeststatus build_write_request(struct request *rq);enum requeststatus build_rq_buffer(struct rqelement *rqe, struct plex *plex);void freerq(struct request *rq);int find_alternate_sd(struct request *rq);int check_range_covered(struct request *);void complete_rqe(struct buf *bp);void complete_raid5_write(struct rqelement *);int abortrequest(struct request *rq, int error);void sdio_done(struct buf *bp);int vinum_bounds_check(struct buf *bp, struct volume *vol);caddr_t allocdatabuf(struct rqelement *rqe);void freedatabuf(struct rqelement *rqe);#ifdef VINUMDEBUGstruct rqinfo rqinfo[RQINFO_SIZE];struct rqinfo *rqip = rqinfo;void logrq(enum rqinfo_type type, union rqinfou info, struct buf *ubp){    int s = splhigh();    microtime(&rqip->timestamp);			    /* when did this happen? */    rqip->type = type;    rqip->bp = ubp;					    /* user buffer */    switch (type) {    case loginfo_user_bp:    case loginfo_user_bpl:	bcopy(info.bp, &rqip->info.b, sizeof(struct buf));	break;    case loginfo_iodone:    case loginfo_rqe:    case loginfo_raid5_data:    case loginfo_raid5_parity:	bcopy(info.rqe, &rqip->info.rqe, sizeof(struct rqelement));	break;    case loginfo_unused:	break;    }    rqip++;    if (rqip >= &rqinfo[RQINFO_SIZE])			    /* wrap around */	rqip = rqinfo;    splx(s);}#endifvoid vinumstrategy(struct buf *bp){    int volno;    struct volume *vol = NULL;    struct devcode *device = (struct devcode *) &bp->b_dev; /* decode device number */    switch (device->type) {    case VINUM_SD_TYPE:    case VINUM_RAWSD_TYPE:	sdio(bp);	return;	/*	 * In fact, vinum doesn't handle drives: they're	 * handled directly by the disk drivers	 */    case VINUM_DRIVE_TYPE:    default:	bp->b_error = EIO;				    /* I/O error */	bp->b_flags |= B_ERROR;	biodone(bp);	return;    case VINUM_VOLUME_TYPE:				    /* volume I/O */	volno = Volno(bp->b_dev);	vol = &VOL[volno];	if (vol->state != volume_up) {			    /* can't access this volume */	    bp->b_error = EIO;				    /* I/O error */	    bp->b_flags |= B_ERROR;	    biodone(bp);	    return;	}	if (vinum_bounds_check(bp, vol) <= 0) {		    /* don't like them bounds */	    biodone(bp);				    /* have nothing to do with this */	    return;	}	/* FALLTHROUGH */	/*	 * Plex I/O is pretty much the same as volume I/O	 * for a single plex.  Indicate this by passing a NULL	 * pointer (set above) for the volume	 */    case VINUM_PLEX_TYPE:    case VINUM_RAWPLEX_TYPE:	bp->b_resid = bp->b_bcount;			    /* transfer everything */	vinumstart(bp, 0);	return;    }}/* * Start a transfer.  Return -1 on error, * 0 if OK, 1 if we need to retry. * Parameter reviveok is set when doing * transfers for revives: it allows transfers to * be started immediately when a revive is in * progress.  During revive, normal transfers * are queued if they share address space with * a currently active revive operation. */int vinumstart(struct buf *bp, int reviveok){    int plexno;    int maxplex;					    /* maximum number of plexes to handle */    struct volume *vol;    struct request *rq;					    /* build up our request here */    enum requeststatus status;#if VINUMDEBUG    if (debug & DEBUG_LASTREQS)	logrq(loginfo_user_bp, (union rqinfou) bp, bp);#endif    /*     * XXX In these routines, we're assuming that     * we will always be called with bp->b_bcount     * which is a multiple of the sector size.  This     * is a reasonable assumption, since we are only     * called from system routines.  Should we check     * anyway?     */    if ((bp->b_bcount % DEV_BSIZE) != 0) {		    /* bad length */	bp->b_error = EINVAL;				    /* invalid size */	bp->b_flags |= B_ERROR;	biodone(bp);	return -1;    }    rq = (struct request *) Malloc(sizeof(struct request)); /* allocate a request struct */    if (rq == NULL) {					    /* can't do it */	bp->b_error = ENOMEM;				    /* can't get memory */	bp->b_flags |= B_ERROR;	biodone(bp);	return -1;    }    bzero(rq, sizeof(struct request));    /*     * Note the volume ID.  This can be NULL, which     * the request building functions use as an     * indication for single plex I/O     */    rq->bp = bp;					    /* and the user buffer struct */    if (DEVTYPE(bp->b_dev) == VINUM_VOLUME_TYPE) {	    /* it's a volume, */	rq->volplex.volno = Volno(bp->b_dev);		    /* get the volume number */	vol = &VOL[rq->volplex.volno];			    /* and point to it */	vol->active++;					    /* one more active request */	maxplex = vol->plexes;				    /* consider all its plexes */    } else {	vol = NULL;					    /* no volume */	rq->volplex.plexno = Plexno(bp->b_dev);		    /* point to the plex */	rq->isplex = 1;					    /* note that it's a plex */	maxplex = 1;					    /* just the one plex */    }    if (bp->b_flags & B_READ) {	/*	 * This is a read request.  Decide	 * which plex to read from.	 *	 * There's a potential race condition here,	 * since we're not locked, and we could end	 * up multiply incrementing the round-robin	 * counter.  This doesn't have any serious	 * effects, however.	 */	if (vol != NULL) {	    vol->reads++;	    vol->bytes_read += bp->b_bcount;	    plexno = vol->preferred_plex;		    /* get the plex to use */	    if (plexno < 0) {				    /* round robin */		plexno = vol->last_plex_read;		vol->last_plex_read++;		if (vol->last_plex_read == vol->plexes)	    /* got the the end? */		    vol->last_plex_read = 0;		    /* wrap around */	    }	    status = build_read_request(rq, plexno);	    /* build a request */	} else {	    daddr_t diskaddr = bp->b_blkno;		    /* start offset of transfer */	    status = bre(rq,				    /* build a request list */		rq->volplex.plexno,		&diskaddr,		diskaddr + (bp->b_bcount / DEV_BSIZE));	}	if ((status > REQUEST_RECOVERED)		    /* can't satisfy it */	||(bp->b_flags & B_DONE)) {			    /* XXX shouldn't get this without bad status */	    if (status == REQUEST_DOWN) {		    /* not enough subdisks */		bp->b_error = EIO;			    /* I/O error */		bp->b_flags |= B_ERROR;	    }	    biodone(bp);	    freerq(rq);	    return -1;	}	return launch_requests(rq, reviveok);		    /* now start the requests if we can */    } else	/*	 * This is a write operation.  We write to all	 * plexes.  If this is a RAID 5 plex, we must also	 * update the parity stripe.	 */    {	if (vol != NULL) {	    vol->writes++;	    vol->bytes_written += bp->b_bcount;	    status = build_write_request(rq);		    /* Not all the subdisks are up */	} else {					    /* plex I/O */	    daddr_t diskstart;	    diskstart = bp->b_blkno;			    /* start offset of transfer */	    status = bre(rq,		Plexno(bp->b_dev),		&diskstart,		bp->b_blkno + (bp->b_bcount / DEV_BSIZE));  /* build requests for the plex */	}	if ((status > REQUEST_RECOVERED)		    /* can't satisfy it */	||(bp->b_flags & B_DONE)) {			    /* XXX shouldn't get this without bad status */	    if (status == REQUEST_DOWN) {		    /* not enough subdisks */		bp->b_error = EIO;			    /* I/O error */		bp->b_flags |= B_ERROR;	    }	    if ((bp->b_flags & B_DONE) == 0)		biodone(bp);	    freerq(rq);	    return -1;	}	return launch_requests(rq, reviveok);		    /* now start the requests if we can */    }}/* * Call the low-level strategy routines to * perform the requests in a struct request */int launch_requests(struct request *rq, int reviveok){    struct rqgroup *rqg;    int rqno;						    /* loop index */    struct rqelement *rqe;				    /* current element */    int s;    /*     * First find out whether we're reviving, and the     * request contains a conflict.  If so, we hang     * the request off plex->waitlist of the first     * plex we find which is reviving     */    if ((rq->flags & XFR_REVIVECONFLICT)		    /* possible revive conflict */    &&(!reviveok)) {					    /* and we don't want to do it now, */	struct sd *sd;	struct request *waitlist;			    /* point to the waitlist */	sd = &SD[rq->sdno];	if (sd->waitlist != NULL) {			    /* something there already, */	    waitlist = sd->waitlist;	    while (waitlist->next != NULL)		    /* find the end */		waitlist = waitlist->next;	    waitlist->next = rq;			    /* hook our request there */	} else	    sd->waitlist = rq;				    /* hook our request at the front */#if VINUMDEBUG	if (debug & DEBUG_REVIVECONFLICT)	    log(LOG_DEBUG,		"Revive conflict sd %d: %x\n%s dev 0x%x, offset 0x%x, length %ld\n",

⌨️ 快捷键说明

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