/* * 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); }
/* * 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; }
/* * 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); }