* Copyright (c) 1988 University of Utah.
* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* %sccs.include.redist.c%
* from: Utah $Hdr: cd.c 1.1 90/07/09$
* @(#)cd.c 7.2 (Berkeley) %G%
* "Concatenated" disk driver.
#define cdunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */
((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
free((caddr_t)(bp), M_DEVBUF)
int sc_flags
; /* flags */
size_t sc_size
; /* size of cd */
int sc_ileave
; /* interleave */
int sc_ncdisks
; /* number of components */
struct cdcinfo sc_cinfo
[NCDISKS
]; /* component info */
struct cdiinfo
*sc_itable
; /* interleave table */
int sc_usecnt
; /* number of requests active */
struct buf
*sc_bp
; /* "current" request */
int sc_dk
; /* disk index */
register struct cd_softc
*cs
= &cd_softc
[cd
->cd_unit
];
register struct cdcinfo
*ci
;
if (cddebug
& (CDB_FOLLOW
|CDB_INIT
))
printf("cdinit: unit %d\n", cd
->cd_unit
);
cs
->sc_ileave
= cd
->cd_interleave
;
* Verify that each component piece exists and record
* relevant information about it.
for (ix
= 0; ix
< NCDISKS
; ix
++) {
if ((dev
= cd
->cd_dev
[ix
]) == NODEV
)
* Calculate size (truncated to interleave boundary
if (bdevsw
[major(dev
)].d_psize
) {
size
= (*bdevsw
[major(dev
)].d_psize
)(dev
);
size
-= size
% cs
->sc_ileave
;
if (minsize
== 0 || size
< minsize
)
* If uniform interleave is desired set all sizes to that of
* the smallest component.
if (cd
->cd_flags
& CDF_UNIFORM
) {
ci
< &cs
->sc_cinfo
[cs
->sc_ncdisks
]; ci
++)
cs
->sc_size
= cs
->sc_ncdisks
* minsize
;
* Construct the interleave table
dk_wpms
[cd
->cd_dk
] = 32 * (60 * DEV_BSIZE
/ 2); /* XXX */
printf("cd%d: %d components (%d blocks) concatenated",
cd
->cd_unit
, cs
->sc_ncdisks
, cs
->sc_size
);
printf(", %d block interleave\n", cs
->sc_ileave
);
cs
->sc_flags
= CDF_ALIVE
| CDF_INITED
;
register struct cd_softc
*cs
;
register struct cdcinfo
*ci
, *smallci
;
register struct cdiinfo
*ii
;
register daddr_t bn
, lbn
;
printf("cdinterleave(%x): ileave %d\n", cs
, cs
->sc_ileave
);
* Allocate an interleave table.
* Chances are this is too big, but we don't care.
size
= (cs
->sc_ncdisks
+ 1) * sizeof(struct cdiinfo
);
cs
->sc_itable
= (struct cdiinfo
*)malloc(size
, M_DEVBUF
, M_WAITOK
);
bzero((caddr_t
)cs
->sc_itable
, size
);
* Trivial case: no interleave (actually interleave of disk size).
* Each table entry represent a single component in its entirety.
if (cs
->sc_ileave
== 0) {
for (ix
= 0; ix
< cs
->sc_ncdisks
; ix
++) {
bn
+= cs
->sc_cinfo
[ix
].ci_size
;
printiinfo(cs
->sc_itable
);
* The following isn't fast or pretty; it doesn't have to be.
for (ii
= cs
->sc_itable
; ; ii
++) {
* Locate the smallest of the remaining components
ci
< &cs
->sc_cinfo
[cs
->sc_ncdisks
]; ci
++)
if (ci
->ci_size
> size
&&
ci
->ci_size
< smallci
->ci_size
))
* Record starting logical block and component offset
ii
->ii_startblk
= bn
/ cs
->sc_ileave
;
* Determine how many disks take part in this interleave
* and record their indices.
ci
< &cs
->sc_cinfo
[cs
->sc_ncdisks
]; ci
++)
if (ci
->ci_size
>= smallci
->ci_size
)
ii
->ii_index
[ix
++] = ci
- cs
->sc_cinfo
;
bn
+= ix
* (smallci
->ci_size
- size
);
lbn
= smallci
->ci_size
/ cs
->sc_ileave
;
printiinfo(cs
->sc_itable
);
for (ix
= 0; ii
->ii_ndisk
; ix
++, ii
++) {
printf(" itab[%d]: #dk %d sblk %d soff %d",
ix
, ii
->ii_ndisk
, ii
->ii_startblk
, ii
->ii_startoff
);
for (i
= 0; i
< ii
->ii_ndisk
; i
++)
printf(" %d", ii
->ii_index
[i
]);
register struct cd_softc
*cs
= &cd_softc
[unit
];
if (cddebug
& CDB_FOLLOW
)
printf("cdopen(%x, %x)\n", dev
, flags
);
if (unit
>= NCD
|| (cs
->sc_flags
& CDF_ALIVE
) == 0)
register int unit
= cdunit(bp
->b_dev
);
register struct cd_softc
*cs
= &cd_softc
[unit
];
if (cddebug
& CDB_FOLLOW
)
printf("cdstrategy(%x): unit %d\n", bp
, unit
);
if ((cs
->sc_flags
& CDF_INITED
) == 0) {
sz
= howmany(bp
->b_bcount
, DEV_BSIZE
);
if (bn
< 0 || bn
+ sz
> cs
->sc_size
) {
bp
->b_resid
= bp
->b_bcount
;
bp
->b_bcount
= dbtob(sz
);
bp
->b_resid
= bp
->b_bcount
;
* XXX: the use of sc_bp is just to retain the "traditional"
* interface to the start routine.
register struct cd_softc
*cs
= &cd_softc
[unit
];
register struct buf
*bp
= cs
->sc_bp
;
register long bcount
, rcount
;
if (cddebug
& CDB_FOLLOW
)
printf("cdstart(%d)\n", unit
);
* Instumentation (not real meaningful)
dk_busy
|= 1 << cs
->sc_dk
;
dk_wds
[cs
->sc_dk
] += bp
->b_bcount
>> 6;
* Allocate component buffers and fire off the requests
for (bcount
= bp
->b_bcount
; bcount
> 0; bcount
-= rcount
) {
cbp
= cdbuffer(cs
, bp
, bn
, addr
, bcount
);
(*bdevsw
[major(cbp
->b_dev
)].d_strategy
)(cbp
);
* Build a component buffer header.
cdbuffer(cs
, bp
, bn
, addr
, bcount
)
register struct cd_softc
*cs
;
register struct cdcinfo
*ci
;
register struct buf
*cbp
;
register daddr_t cbn
, cboff
;
printf("cdbuffer(%x, %x, %d, %x, %d)\n",
cs
, bp
, bn
, addr
, bcount
);
* Determine which component bn falls in.
if (cs
->sc_ileave
== 0) {
for (ci
= cs
->sc_cinfo
; cbn
>= sblk
+ ci
->ci_size
; ci
++)
register struct cdiinfo
*ii
;
cboff
= cbn
% cs
->sc_ileave
;
for (ii
= cs
->sc_itable
; ii
->ii_ndisk
; ii
++)
if (ii
->ii_startblk
> cbn
)
off
= cbn
- ii
->ii_startblk
;
cbn
= ii
->ii_startoff
+ off
;
cdisk
= ii
->ii_index
[off
% ii
->ii_ndisk
];
cbn
= ii
->ii_startoff
+ off
/ ii
->ii_ndisk
;
ci
= &cs
->sc_cinfo
[cdisk
];
* Fill in the component buf structure.
cbp
->b_flags
= bp
->b_flags
| B_CALL
;
cbp
->b_iodone
= cdiodone
;
cbp
->b_proc
= bp
->b_proc
;
cbp
->b_blkno
= cbn
+ cboff
;
cbp
->b_bcount
= dbtob(ci
->ci_size
- cbn
);
cbp
->b_bcount
= dbtob(cs
->sc_ileave
- cboff
);
if (cbp
->b_bcount
> bcount
)
* XXX: context for cdiodone
cbp
->b_saveaddr
= (caddr_t
)bp
;
cbp
->b_pfcent
= ((cs
- cd_softc
) << 16) | (ci
- cs
->sc_cinfo
);
printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
ci
->ci_dev
, ci
-cs
->sc_cinfo
, cbp
, cbp
->b_blkno
,
cbp
->b_un
.b_addr
, cbp
->b_bcount
);
register struct cd_softc
*cs
= &cd_softc
[unit
];
register struct buf
*bp
= cs
->sc_bp
;
if (cddebug
& CDB_FOLLOW
)
printf("cdintr(%d): bp %x\n", unit
, bp
);
* Request is done for better or worse, wakeup the top half.
if (--cs
->sc_usecnt
== 0 && cs
->sc_dk
>= 0)
dk_busy
&= ~(1 << cs
->sc_dk
);
if (bp
->b_flags
& B_ERROR
)
bp
->b_resid
= bp
->b_bcount
;
* Called by biodone at interrupt time.
* Mark the component as done and if all components are done,
register struct buf
*cbp
;
register struct buf
*bp
= (struct buf
*)cbp
->b_saveaddr
;/* XXX */
register int unit
= (cbp
->b_pfcent
>> 16) & 0xFFFF; /* XXX */
if (cddebug
& CDB_FOLLOW
)
printf("cdiodone(%x)\n", cbp
);
printf("cdiodone: bp %x bcount %d resid %d\n",
bp
, bp
->b_bcount
, bp
->b_resid
);
printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
cbp
->b_dev
, cbp
->b_pfcent
& 0xFFFF, cbp
,
cbp
->b_blkno
, cbp
->b_un
.b_addr
, cbp
->b_bcount
);
if (cbp
->b_flags
& B_ERROR
) {
bp
->b_error
= biowait(cbp
);
printf("cd%d: error %d on component %d\n",
unit
, bp
->b_error
, cbp
->b_pfcent
& 0xFFFF);
* If all done, "interrupt".
* Again, sc_bp is only used to preserve the traditional interface.
panic("cdiodone: count");
cd_softc
[unit
].sc_bp
= bp
;
register int unit
= cdunit(dev
);
if (cddebug
& CDB_FOLLOW
)
printf("cdread(%x, %x)\n", dev
, uio
);
return(physio(cdstrategy
, &cdbuf
[unit
], dev
, B_READ
, minphys
, uio
));
register int unit
= cdunit(dev
);
if (cddebug
& CDB_FOLLOW
)
printf("cdwrite(%x, %x)\n", dev
, uio
);
return(physio(cdstrategy
, &cdbuf
[unit
], dev
, B_WRITE
, minphys
, uio
));
cdioctl(dev
, cmd
, data
, flag
)
register struct cd_softc
*cs
= &cd_softc
[unit
];
if (unit
>= NCD
|| (cs
->sc_flags
& CDF_INITED
) == 0)