Ejemplo n.º 1
0
/*
 * Build a component buffer header.
 */
long
ccdbuffer(struct ccd_softc *cs, struct buf *bp, daddr_t bn, caddr_t addr,
    long bcount, struct ccdbuf **cbpp, int old_io)
{
	struct ccdcinfo *ci, *ci2 = NULL;
	struct ccdbuf *cbp;
	daddr_t cbn, cboff, sblk;
	int ccdisk, ccdisk2, off;
	long old_bcount, cnt;
	struct ccdiinfo *ii;
	struct buf *nbp;

	CCD_DPRINTF(CCDB_IO, ("ccdbuffer(%p, %p, %d, %p, %ld, %p)\n",
	    cs, bp, bn, addr, bcount, cbpp));

	/*
	 * Determine which component bn falls in.
	 */
	cbn = bn;
	cboff = 0;

	if (cs->sc_ileave == 0) {
		/*
		 * Serially concatenated
		 */
		sblk = 0;
		for (ccdisk = 0, ci = &cs->sc_cinfo[ccdisk];
		    cbn >= sblk + ci->ci_size;
		    ccdisk++, ci = &cs->sc_cinfo[ccdisk])
			sblk += ci->ci_size;
		cbn -= sblk;
	} else {
		/*
		 * Interleaved
		 */
		cboff = cbn % cs->sc_ileave;
		cbn /= cs->sc_ileave;
		for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
			if (ii->ii_startblk > cbn)
				break;
		ii--;
		off = cbn - ii->ii_startblk;
		if (ii->ii_ndisk == 1) {
			ccdisk = ii->ii_index[0];
			cbn = ii->ii_startoff + off;
		} else {
			ccdisk = ii->ii_index[off % ii->ii_ndisk];
			cbn = ii->ii_startoff + off / ii->ii_ndisk;
		}
		if (cs->sc_cflags & CCDF_MIRROR) {
			/* Mirrored data */
			ccdisk2 = ccdisk + ii->ii_ndisk;
			ci2 = &cs->sc_cinfo[ccdisk2];
			/* spread the read over both parts */
			if (bp->b_flags & B_READ &&
			    bcount > bp->b_bcount / 2 &&
			    (!(ci2->ci_flags & CCIF_FAILED) ||
			      ci->ci_flags & CCIF_FAILED))
				ccdisk = ccdisk2;
		}
		cbn *= cs->sc_ileave;
		ci = &cs->sc_cinfo[ccdisk];
		CCD_DPRINTF(CCDB_IO, ("ccdisk %d cbn %d ci %p ci2 %p\n",
		    ccdisk, cbn, ci, ci2));
	}

	/* Limit the operation at next component border */
	if (cs->sc_ileave == 0)
		cnt = dbtob(ci->ci_size - cbn);
	else
		cnt = dbtob(cs->sc_ileave - cboff);
	if (cnt < bcount)
		bcount = cnt;

	if (old_io || cbpp[ccdisk] == NULL) {
		/*
		 * Setup new component buffer.
		 */
		cbp = cbpp[old_io ? 0 : ccdisk] = getccdbuf();
		cbp->cb_flags = old_io ? CBF_OLD : 0;
		nbp = &cbp->cb_buf;
		nbp->b_flags = bp->b_flags | B_CALL;
		nbp->b_iodone = ccdiodone;
		nbp->b_proc = bp->b_proc;
		nbp->b_dev = ci->ci_dev;		/* XXX */
		nbp->b_blkno = cbn + cboff;
		nbp->b_vp = ci->ci_vp;
		nbp->b_bcount = bcount;
		LIST_INIT(&nbp->b_dep);

		/*
		 * context for ccdiodone
		 */
		cbp->cb_obp = bp;
		cbp->cb_sc = cs;
		cbp->cb_comp = ccdisk;

		/* Deal with the different algorithms */
		if (old_io)
			nbp->b_data = addr;
		else {
			do {
				nbp->b_data = (caddr_t) uvm_km_valloc(ccdmap,
				    bp->b_bcount);

				/*
				 * XXX Instead of sleeping, we might revert
				 * XXX to old I/O policy for this buffer set.
				 */
				if (nbp->b_data == NULL) {
					ccd_need_kvm++;
					tsleep(ccdmap, PRIBIO, "ccdbuffer", 0);
				}
			} while (nbp->b_data == NULL);
			cbp->cb_sgcnt = 0;
			old_bcount = 0;
		}

		/*
		 * Mirrors have an additional write operation that is nearly
		 * identical to the first.
		 */
		if ((cs->sc_cflags & CCDF_MIRROR) &&
		    !(ci2->ci_flags & CCIF_FAILED) &&
		    ((cbp->cb_buf.b_flags & B_READ) == 0)) {
			struct ccdbuf *cbp2;
			cbpp[old_io? 1 : ccdisk2] = cbp2 = getccdbuf();
			*cbp2 = *cbp;
			cbp2->cb_flags = CBF_MIRROR | (old_io ? CBF_OLD : 0);
			cbp2->cb_buf.b_dev = ci2->ci_dev;	/* XXX */
			cbp2->cb_buf.b_vp = ci2->ci_vp;
			LIST_INIT(&cbp2->cb_buf.b_dep);
			cbp2->cb_comp = ccdisk2;
			cbp2->cb_dep = cbp;
			cbp->cb_dep = cbp2;
		}
	} else {
		/*
		 * Continue on an already started component buffer
		 */
		cbp = cbpp[ccdisk];
		nbp = &cbp->cb_buf;

		/*
		 * Map the new pages at the end of the buffer.
		 */
		old_bcount = nbp->b_bcount;
		nbp->b_bcount += bcount;
	}

	if (!old_io) {
		CCD_DPRINTF(CCDB_IO, ("ccdbuffer: sg %d (%p/%x) off %x\n",
		    cbp->cb_sgcnt, addr, bcount, old_bcount));

		pagemove(addr, nbp->b_data + old_bcount, round_page(bcount));
		nbp->b_bufsize += round_page(bcount);
		cbp->cb_sg[cbp->cb_sgcnt].cs_sgaddr = addr;
		cbp->cb_sg[cbp->cb_sgcnt].cs_sglen = bcount;
		cbp->cb_sgcnt++;
	}

	CCD_DPRINTF(CCDB_IO, (" dev %x(u%d): cbp %p bn %d addr %p bcnt %ld\n",
	    ci->ci_dev, ci-cs->sc_cinfo, cbp, bp->b_blkno,
	    bp->b_data, bp->b_bcount));

	return (bcount);
}
Ejemplo n.º 2
0
/*
 * Expand or contract the actual memory allocated to a buffer.
 *
 * If the buffer shrinks, data is lost, so it's up to the
 * caller to have written it out *first*; this routine will not
 * start a write.  If the buffer grows, it's the callers
 * responsibility to fill out the buffer's additional contents.
 */
void
allocbuf(struct buf *bp, int size)
{
	struct buf	*nbp;
	vsize_t		desired_size;
	int		s;

	desired_size = round_page(size);
	if (desired_size > MAXBSIZE)
		panic("allocbuf: buffer larger than MAXBSIZE requested");

	if (bp->b_bufsize == desired_size)
		goto out;

	/*
	 * If the buffer is smaller than the desired size, we need to snarf
	 * it from other buffers.  Get buffers (via getnewbuf()), and
	 * steal their pages.
	 */
	while (bp->b_bufsize < desired_size) {
		int amt;

		/* find a buffer */
		getnewbuf(0, 0, &nbp);
 		SET(nbp->b_flags, B_INVAL);
		binshash(nbp, &invalhash);

		/* and steal its pages, up to the amount we need */
		amt = MIN(nbp->b_bufsize, (desired_size - bp->b_bufsize));
		pagemove((nbp->b_data + nbp->b_bufsize - amt),
			 bp->b_data + bp->b_bufsize, amt);
		bp->b_bufsize += amt;
		nbp->b_bufsize -= amt;

		/* reduce transfer count if we stole some data */
		if (nbp->b_bcount > nbp->b_bufsize)
			nbp->b_bcount = nbp->b_bufsize;

#ifdef DIAGNOSTIC
		if (nbp->b_bufsize < 0)
			panic("allocbuf: negative bufsize");
#endif

		brelse(nbp);
	}

	/*
	 * If we want a buffer smaller than the current size,
	 * shrink this buffer.  Grab a buf head from the EMPTY queue,
	 * move a page onto it, and put it on front of the AGE queue.
	 * If there are no free buffer headers, leave the buffer alone.
	 */
	if (bp->b_bufsize > desired_size) {
		s = splbio();
		if ((nbp = bufqueues[BQ_EMPTY].tqh_first) == NULL) {
			/* No free buffer head */
			splx(s);
			goto out;
		}
		bremfree(nbp);
		SET(nbp->b_flags, B_BUSY);
		splx(s);

		/* move the page to it and note this change */
		pagemove(bp->b_data + desired_size,
		    nbp->b_data, bp->b_bufsize - desired_size);
		nbp->b_bufsize = bp->b_bufsize - desired_size;
		bp->b_bufsize = desired_size;
		nbp->b_bcount = 0;
		SET(nbp->b_flags, B_INVAL);

		/* release the newly-filled buffer and leave */
		brelse(nbp);
	}

out:
	bp->b_bcount = size;
}
Ejemplo n.º 3
0
/*
 * Called at interrupt time.
 * Mark the component as done and if all components are done,
 * take a ccd interrupt.
 */
void
ccdiodone(struct buf *vbp)
{
	struct ccdbuf *cbp = (struct ccdbuf *)vbp;
	struct buf *bp = cbp->cb_obp;
	struct ccd_softc *cs = cbp->cb_sc;
	int old_io = cbp->cb_flags & CBF_OLD;
	int i;
	long count = bp->b_bcount, off;
	char *comptype;

	splassert(IPL_BIO);

	CCD_DPRINTF(CCDB_FOLLOW, ("ccdiodone(%p)\n", cbp));
	CCD_DPRINTF(CCDB_IO, (cbp->cb_flags & CBF_MIRROR?
	    "ccdiodone: mirror component\n" : 
	    "ccdiodone: bp %p bcount %ld resid %ld\n",
	    bp, bp->b_bcount, bp->b_resid));
	CCD_DPRINTF(CCDB_IO, (" dev %x(u%d), cbp %p bn %d addr %p bcnt %ld\n",
	    vbp->b_dev, cbp->cb_comp, cbp, vbp->b_blkno,
	    vbp->b_data, vbp->b_bcount));

	if (vbp->b_flags & B_ERROR) {
		cs->sc_cinfo[cbp->cb_comp].ci_flags |= CCIF_FAILED;
		if (cbp->cb_flags & CBF_MIRROR)
			comptype = " (mirror)";
		else {
			bp->b_flags |= B_ERROR;
			bp->b_error = vbp->b_error ?
			    vbp->b_error : EIO;
			comptype = "";
		}

		printf("%s: error %d on component %d%s\n",
		    cs->sc_xname, bp->b_error, cbp->cb_comp, comptype);
	}
	cbp->cb_flags |= CBF_DONE;

	if (cbp->cb_dep &&
	    (cbp->cb_dep->cb_flags & CBF_DONE) != (cbp->cb_flags & CBF_DONE))
		return;

	if (cbp->cb_flags & CBF_MIRROR &&
	    !(cbp->cb_dep->cb_flags & CBF_MIRROR)) {
		cbp = cbp->cb_dep;
		vbp = (struct buf *)cbp;
	}

	if (!old_io) {
		/*
		 * Gather all the pieces and put them where they should be.
		 */
		for (i = 0, off = 0; i < cbp->cb_sgcnt; i++) {
			CCD_DPRINTF(CCDB_IO,
			    ("ccdiodone: sg %d (%p/%x) off %x\n", i,
			    cbp->cb_sg[i].cs_sgaddr,
			    cbp->cb_sg[i].cs_sglen, off));
			pagemove(vbp->b_data + off, cbp->cb_sg[i].cs_sgaddr,
			    round_page(cbp->cb_sg[i].cs_sglen));
			off += cbp->cb_sg[i].cs_sglen;
		}

		uvm_km_free(ccdmap, (vaddr_t)vbp->b_data, count);
		if (ccd_need_kvm) {
			ccd_need_kvm = 0;
			wakeup(ccdmap);
		}
	}
	count = vbp->b_bcount;

	putccdbuf(cbp);
	if (cbp->cb_dep)
		putccdbuf(cbp->cb_dep);

	/*
	 * If all done, "interrupt".
	 *
	 * Note that mirror component buffers aren't counted against
	 * the original I/O buffer.
	 */
	if (count > bp->b_resid)
		panic("ccdiodone: count");
	bp->b_resid -= count;
	if (bp->b_resid == 0)
		ccdintr(cs, bp);
}