Add locking to mcd(4) and mark MPSAFE.

- Actually use existing per-softc mutex.
- Use mutex in cdev routines and remove D_NEEDGIANT.
- Use callout(9) instead of timeout(9).
- Don't check for impossible conditions (e.g. MCDINIT being clear).
- Remove critical_enter/exit when sending a PIO command.
- Use bus_*() instead of bus_space_*().

Tested by:	no one
This commit is contained in:
John Baldwin 2014-11-18 21:51:01 +00:00
parent 2a0db815fe
commit 23c31a3c5d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=274676
3 changed files with 117 additions and 70 deletions

View File

@ -128,7 +128,7 @@ static void hsg2msf(int hsg, bcd_t *msf);
static int msf2hsg(bcd_t *msf, int relative);
static int mcd_volinfo(struct mcd_softc *);
static int mcd_waitrdy(struct mcd_softc *,int dly);
static timeout_t mcd_timeout;
static void mcd_timeout(void *arg);
static void mcd_doread(struct mcd_softc *, int state, struct mcd_mbx *mbxin);
static void mcd_soft_reset(struct mcd_softc *);
static int mcd_hard_reset(struct mcd_softc *);
@ -168,7 +168,7 @@ static struct cdevsw mcd_cdevsw = {
.d_ioctl = mcdioctl,
.d_strategy = mcdstrategy,
.d_name = "mcd",
.d_flags = D_DISK | D_NEEDGIANT,
.d_flags = D_DISK,
};
#define MCD_RETRYS 5
@ -193,6 +193,7 @@ mcd_attach(struct mcd_softc *sc)
unit = device_get_unit(sc->dev);
MCD_LOCK(sc);
sc->data.flags |= MCDINIT;
mcd_soft_reset(sc);
bioq_init(&sc->data.head);
@ -201,11 +202,13 @@ mcd_attach(struct mcd_softc *sc)
/* wire controller for interrupts and dma */
mcd_configure(sc);
#endif
MCD_UNLOCK(sc);
/* name filled in probe */
sc->mcd_dev_t = make_dev(&mcd_cdevsw, 8 * unit,
UID_ROOT, GID_OPERATOR, 0640, "mcd%d", unit);
sc->mcd_dev_t->si_drv1 = (void *)sc;
callout_init_mtx(&sc->timer, &sc->mtx, 0);
return (0);
}
@ -218,41 +221,49 @@ mcdopen(struct cdev *dev, int flags, int fmt, struct thread *td)
sc = (struct mcd_softc *)dev->si_drv1;
/* not initialized*/
if (!(sc->data.flags & MCDINIT))
return (ENXIO);
/* invalidated in the meantime? mark all open part's invalid */
if (!(sc->data.flags & MCDVALID) && sc->data.openflags)
MCD_LOCK(sc);
if (!(sc->data.flags & MCDVALID) && sc->data.openflags) {
MCD_UNLOCK(sc);
return (ENXIO);
}
if (mcd_getstat(sc, 1) == -1)
if (mcd_getstat(sc, 1) == -1) {
MCD_UNLOCK(sc);
return (EIO);
}
if ( (sc->data.status & (MCDDSKCHNG|MCDDOOROPEN))
|| !(sc->data.status & MCDDSKIN))
for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) {
(void) tsleep((caddr_t)sc, PSOCK | PCATCH, "mcdsn1", hz/WAIT_FRAC);
if ((r = mcd_getstat(sc, 1)) == -1)
(void) mtx_sleep(sc, &sc->mtx, PSOCK | PCATCH,
"mcdsn1", hz/WAIT_FRAC);
if ((r = mcd_getstat(sc, 1)) == -1) {
MCD_UNLOCK(sc);
return (EIO);
}
if (r != -2)
break;
}
if (sc->data.status & MCDDOOROPEN) {
MCD_UNLOCK(sc);
device_printf(sc->dev, "door is open\n");
return (ENXIO);
}
if (!(sc->data.status & MCDDSKIN)) {
MCD_UNLOCK(sc);
device_printf(sc->dev, "no CD inside\n");
return (ENXIO);
}
if (sc->data.status & MCDDSKCHNG) {
MCD_UNLOCK(sc);
device_printf(sc->dev, "CD not sensed\n");
return (ENXIO);
}
if (mcd_size(dev) < 0) {
MCD_UNLOCK(sc);
device_printf(sc->dev, "failed to get disk size\n");
return (ENXIO);
}
@ -262,10 +273,14 @@ mcdopen(struct cdev *dev, int flags, int fmt, struct thread *td)
sc->data.flags |= MCDVALID;
(void) mcd_lock_door(sc, MCD_LK_LOCK);
if (!(sc->data.flags & MCDVALID))
if (!(sc->data.flags & MCDVALID)) {
MCD_UNLOCK(sc);
return (ENXIO);
}
return mcd_read_toc(sc);
r = mcd_read_toc(sc);
MCD_UNLOCK(sc);
return (r);
}
static int
@ -275,12 +290,13 @@ mcdclose(struct cdev *dev, int flags, int fmt, struct thread *td)
sc = (struct mcd_softc *)dev->si_drv1;
if (!(sc->data.flags & MCDINIT) || !sc->data.openflags)
return (ENXIO);
MCD_LOCK(sc);
KASSERT(sc->data.openflags, ("device not open"));
(void) mcd_lock_door(sc, MCD_LK_UNLOCK);
sc->data.openflags = 0;
sc->data.partflags &= ~MCDREADRAW;
MCD_UNLOCK(sc);
return (0);
}
@ -293,6 +309,7 @@ mcdstrategy(struct bio *bp)
sc = (struct mcd_softc *)bp->bio_dev->si_drv1;
/* if device invalidated (e.g. media change, door open), error */
MCD_LOCK(sc);
if (!(sc->data.flags & MCDVALID)) {
device_printf(sc->dev, "media changed\n");
bp->bio_error = EIO;
@ -321,11 +338,13 @@ mcdstrategy(struct bio *bp)
/* now check whether we can perform processing */
mcd_start(sc);
MCD_UNLOCK(sc);
return;
bad:
bp->bio_flags |= BIO_ERROR;
done:
MCD_UNLOCK(sc);
bp->bio_resid = bp->bio_bcount;
biodone(bp);
return;
@ -336,6 +355,7 @@ mcd_start(struct mcd_softc *sc)
{
struct bio *bp;
MCD_ASSERT_LOCKED(sc);
if (sc->data.flags & MCDMBXBSY) {
return;
}
@ -365,8 +385,11 @@ mcdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *t
sc = (struct mcd_softc *)dev->si_drv1;
if (mcd_getstat(sc, 1) == -1) /* detect disk change too */
MCD_LOCK(sc);
if (mcd_getstat(sc, 1) == -1) { /* detect disk change too */
MCD_UNLOCK(sc);
return (EIO);
}
MCD_TRACE("ioctl called 0x%lx\n", cmd);
switch (cmd) {
@ -378,83 +401,114 @@ MCD_TRACE("ioctl called 0x%lx\n", cmd);
case CDIOCSETMUTE:
case CDIOCSETLEFT:
case CDIOCSETRIGHT:
MCD_UNLOCK(sc);
return (EINVAL);
case CDIOCEJECT:
return mcd_eject(sc);
r = mcd_eject(sc);
MCD_UNLOCK(sc);
return (r);
case CDIOCSETDEBUG:
sc->data.debug = 1;
MCD_UNLOCK(sc);
return (0);
case CDIOCCLRDEBUG:
sc->data.debug = 0;
MCD_UNLOCK(sc);
return (0);
case CDIOCRESET:
return mcd_hard_reset(sc);
r = mcd_hard_reset(sc);
MCD_UNLOCK(sc);
return (r);
case CDIOCALLOW:
return mcd_lock_door(sc, MCD_LK_UNLOCK);
r = mcd_lock_door(sc, MCD_LK_UNLOCK);
MCD_UNLOCK(sc);
return (r);
case CDIOCPREVENT:
return mcd_lock_door(sc, MCD_LK_LOCK);
r = mcd_lock_door(sc, MCD_LK_LOCK);
MCD_UNLOCK(sc);
return (r);
case CDIOCCLOSE:
return mcd_inject(sc);
r = mcd_inject(sc);
MCD_UNLOCK(sc);
return (r);
}
if (!(sc->data.flags & MCDVALID)) {
if ( (sc->data.status & (MCDDSKCHNG|MCDDOOROPEN))
|| !(sc->data.status & MCDDSKIN))
for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) {
(void) tsleep((caddr_t)sc, PSOCK | PCATCH, "mcdsn2", hz/WAIT_FRAC);
if ((r = mcd_getstat(sc, 1)) == -1)
(void) mtx_sleep(sc, &sc->mtx, PSOCK | PCATCH,
"mcdsn2", hz/WAIT_FRAC);
if ((r = mcd_getstat(sc, 1)) == -1) {
MCD_UNLOCK(sc);
return (EIO);
}
if (r != -2)
break;
}
if ( (sc->data.status & (MCDDOOROPEN|MCDDSKCHNG))
|| !(sc->data.status & MCDDSKIN)
|| mcd_size(dev) < 0
)
) {
MCD_UNLOCK(sc);
return (ENXIO);
}
sc->data.flags |= MCDVALID;
sc->data.partflags |= MCDREADRAW;
(void) mcd_lock_door(sc, MCD_LK_LOCK);
if (!(sc->data.flags & MCDVALID))
if (!(sc->data.flags & MCDVALID)) {
MCD_UNLOCK(sc);
return (ENXIO);
}
}
switch (cmd) {
case DIOCGMEDIASIZE:
*(off_t *)addr = (off_t)sc->data.disksize * sc->data.blksize;
return (0);
r = 0;
break;
case DIOCGSECTORSIZE:
*(u_int *)addr = sc->data.blksize;
return (0);
r = 0;
break;
case CDIOCPLAYTRACKS:
return mcd_playtracks(sc, (struct ioc_play_track *) addr);
r = mcd_playtracks(sc, (struct ioc_play_track *) addr);
break;
case CDIOCPLAYBLOCKS:
return mcd_playblocks(sc, (struct ioc_play_blocks *) addr);
r = mcd_playblocks(sc, (struct ioc_play_blocks *) addr);
break;
case CDIOCPLAYMSF:
return mcd_playmsf(sc, (struct ioc_play_msf *) addr);
r = mcd_playmsf(sc, (struct ioc_play_msf *) addr);
break;
case CDIOCREADSUBCHANNEL_SYSSPACE:
return mcd_subchan(sc, (struct ioc_read_subchannel *) addr, 1);
case CDIOCREADSUBCHANNEL:
return mcd_subchan(sc, (struct ioc_read_subchannel *) addr, 0);
case CDIOREADTOCHEADER:
return mcd_toc_header(sc, (struct ioc_toc_header *) addr);
r = mcd_toc_header(sc, (struct ioc_toc_header *) addr);
break;
case CDIOREADTOCENTRYS:
return mcd_toc_entrys(sc, (struct ioc_read_toc_entry *) addr);
case CDIOCRESUME:
return mcd_resume(sc);
r = mcd_resume(sc);
break;
case CDIOCPAUSE:
return mcd_pause(sc);
r = mcd_pause(sc);
break;
case CDIOCSTART:
if (mcd_setmode(sc, MCD_MD_COOKED) != 0)
return (EIO);
return (0);
r = EIO;
else
r = 0;
break;
case CDIOCSTOP:
return mcd_stop(sc);
r = mcd_stop(sc);
break;
default:
return (ENOTTY);
r = ENOTTY;
}
/*NOTREACHED*/
MCD_UNLOCK(sc);
return (r);
}
static int
@ -762,6 +816,7 @@ mcd_timeout(void *arg)
sc = (struct mcd_softc *)arg;
MCD_ASSERT_LOCKED(sc);
mcd_doread(sc, sc->ch_state, sc->ch_mbxsave);
}
@ -775,6 +830,7 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
int blknum;
caddr_t addr;
MCD_ASSERT_LOCKED(sc);
mbx = (state!=MCD_S_BEGIN) ? sc->ch_mbxsave : mbxin;
bp = mbx->bp;
@ -789,15 +845,16 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDGETSTAT);
mbx->count = RDELAY_WAITSTAT;
sc->ch_state = MCD_S_WAITSTAT;
sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */
callout_reset(&sc->timer, hz/100, mcd_timeout, sc); /* XXX */
return;
case MCD_S_WAITSTAT:
sc->ch_state = MCD_S_WAITSTAT;
untimeout(mcd_timeout,(caddr_t)sc, sc->ch);
callout_stop(&sc->timer);
if (mbx->count-- >= 0) {
if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) {
sc->ch_state = MCD_S_WAITSTAT;
timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */
callout_reset(&sc->timer, hz/100,
mcd_timeout, sc); /* XXX */
return;
}
sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF;
@ -834,7 +891,7 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
MCD_WRITE(sc, MCD_REG_COMMAND, rm);
sc->ch_state = MCD_S_WAITMODE;
sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */
callout_reset(&sc->timer, hz / 100, mcd_timeout, sc); /* XXX */
return;
} else {
device_printf(sc->dev, "timeout getstatus\n");
@ -843,14 +900,14 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
case MCD_S_WAITMODE:
sc->ch_state = MCD_S_WAITMODE;
untimeout(mcd_timeout, (caddr_t)sc, sc->ch);
callout_stop(&sc->timer);
if (mbx->count-- < 0) {
device_printf(sc->dev, "timeout set mode\n");
goto readerr;
}
if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) {
sc->ch_state = MCD_S_WAITMODE;
sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100);
callout_reset(&sc->timer, hz / 100, mcd_timeout, sc);
return;
}
sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF;
@ -878,7 +935,6 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
hsg2msf(blknum,rbuf.start_msf);
retry_read:
/* send the read command */
critical_enter();
MCD_WRITE(sc, MCD_REG_COMMAND, sc->data.read_command);
MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[0]);
MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[1]);
@ -886,7 +942,6 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
MCD_WRITE(sc, MCD_REG_COMMAND, 0);
MCD_WRITE(sc, MCD_REG_COMMAND, 0);
MCD_WRITE(sc, MCD_REG_COMMAND, 1);
critical_exit();
/* Spin briefly (<= 2ms) to avoid missing next block */
for (i = 0; i < 20; i++) {
@ -898,11 +953,11 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
mbx->count = RDELAY_WAITREAD;
sc->ch_state = MCD_S_WAITREAD;
sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */
callout_reset(&sc->timer, hz / 100, mcd_timeout, sc); /* XXX */
return;
case MCD_S_WAITREAD:
sc->ch_state = MCD_S_WAITREAD;
untimeout(mcd_timeout, (caddr_t)sc, sc->ch);
callout_stop(&sc->timer);
if (mbx->count-- > 0) {
k = MCD_READ(sc, MCD_FLAGS);
if (!(k & MFL_DATA_NOT_AVAIL)) { /* XXX */
@ -947,7 +1002,7 @@ mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin)
goto changed;
}
sc->ch_state = MCD_S_WAITREAD;
sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */
callout_reset(&sc->timer, hz / 100, mcd_timeout, sc); /* XXX */
return;
} else {
device_printf(sc->dev, "timeout read data\n");
@ -1010,7 +1065,8 @@ mcd_close_tray(struct mcd_softc *sc)
MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDCLOSETRAY);
for (retry = 0; retry < CLOSE_TRAY_SECS * WAIT_FRAC; retry++) {
if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL)
(void) tsleep((caddr_t)sc, PSOCK | PCATCH, "mcdcls", hz/WAIT_FRAC);
(void) mtx_sleep(sc, &sc->mtx, PSOCK | PCATCH,
"mcdcls", hz/WAIT_FRAC);
else {
if ((r = mcd_getstat(sc, 0)) == -1)
return (EIO);
@ -1303,6 +1359,7 @@ mcd_toc_entrys(struct mcd_softc *sc, struct ioc_read_toc_entry *te)
}
/* copy the data back */
MCD_UNLOCK(sc);
return copyout(entries, te->data, n * sizeof(struct cd_toc_entry));
}
@ -1418,6 +1475,7 @@ mcd_subchan(struct mcd_softc *sc, struct ioc_read_subchannel *sch, int nocopyout
break;
}
MCD_UNLOCK(sc);
if (nocopyout == 0)
return copyout(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len));
bcopy(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len));

View File

@ -126,6 +126,7 @@ mcd_alloc_resources (device_t dev)
sc = device_get_softc(dev);
error = 0;
mtx_init(&sc->mtx, "mcd", NULL, MTX_DEF);
if (sc->port_type) {
sc->port = bus_alloc_resource_any(dev, sc->port_type,
@ -135,8 +136,6 @@ mcd_alloc_resources (device_t dev)
error = ENOMEM;
goto bad;
}
sc->port_bst = rman_get_bustag(sc->port);
sc->port_bsh = rman_get_bushandle(sc->port);
}
if (sc->irq_type) {
@ -159,9 +158,6 @@ mcd_alloc_resources (device_t dev)
}
}
mtx_init(&sc->mtx, device_get_nameunit(dev),
"Interrupt lock", MTX_DEF | MTX_RECURSE);
bad:
return (error);
}
@ -175,18 +171,14 @@ mcd_release_resources (device_t dev)
if (sc->irq_ih)
bus_teardown_intr(dev, sc->irq, sc->irq_ih);
if (sc->port) {
if (sc->port)
bus_release_resource(dev, sc->port_type, sc->port_rid, sc->port);
sc->port_bst = 0;
sc->port_bsh = 0;
}
if (sc->irq)
bus_release_resource(dev, sc->irq_type, sc->irq_rid, sc->irq);
if (sc->drq)
bus_release_resource(dev, sc->drq_type, sc->drq_rid, sc->drq);
if (mtx_initialized(&sc->mtx) != 0)
mtx_destroy(&sc->mtx);
mtx_destroy(&sc->mtx);
return;
}

View File

@ -41,8 +41,6 @@ struct mcd_softc {
struct resource * port;
int port_rid;
int port_type;
bus_space_tag_t port_bst;
bus_space_handle_t port_bsh;
struct resource * irq;
int irq_rid;
@ -55,20 +53,19 @@ struct mcd_softc {
struct mtx mtx;
struct callout_handle ch;
struct callout timer;
int ch_state;
struct mcd_mbx * ch_mbxsave;
struct mcd_data data;
};
#define MCD_LOCK(_sc) splx(&(_sc)->mtx
#define MCD_UNLOCK(_sc) splx(&(_sc)->mtx
#define MCD_LOCK(_sc) mtx_lock(&_sc->mtx)
#define MCD_UNLOCK(_sc) mtx_unlock(&_sc->mtx)
#define MCD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
#define MCD_READ(_sc, _reg) \
bus_space_read_1(_sc->port_bst, _sc->port_bsh, _reg)
#define MCD_WRITE(_sc, _reg, _val) \
bus_space_write_1(_sc->port_bst, _sc->port_bsh, _reg, _val)
#define MCD_READ(_sc, _reg) bus_read_1(_sc->port, _reg)
#define MCD_WRITE(_sc, _reg, _val) bus_write_1(_sc->port, _reg, _val)
int mcd_probe (struct mcd_softc *);
int mcd_attach (struct mcd_softc *);