Fix race condition in its_cmd_send()

its_cmd_send() can be called by multiple CPUs simultaneously.
After the command is pushed to ITS command ring the completion
status is polled using global pointer to the next free ring slot.
Use copied pointer and provide correct locking to avoid spurious
pointer value when concurrent access occurs.

Obtained from: Semihalf
Sponsored by:  The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D3436
This commit is contained in:
Zbigniew Bodek 2015-08-26 12:32:46 +00:00
parent d7deff23cd
commit bb2295eb2c

View File

@ -1311,16 +1311,16 @@ its_cmd_wait_completion(struct gic_v3_its_softc *sc, struct its_cmd *cmd_first,
static int
its_cmd_send(struct gic_v3_its_softc *sc, struct its_cmd_desc *desc)
{
struct its_cmd *cmd, *cmd_sync;
struct its_cmd *cmd, *cmd_sync, *cmd_write;
struct its_col col_sync;
struct its_cmd_desc desc_sync;
uint64_t target, cwriter;
mtx_lock_spin(&sc->its_spin_mtx);
cmd = its_cmd_alloc_locked(sc);
mtx_unlock_spin(&sc->its_spin_mtx);
if (cmd == NULL) {
device_printf(sc->dev, "could not allocate ITS command\n");
mtx_unlock_spin(&sc->its_spin_mtx);
return (EBUSY);
}
@ -1328,9 +1328,7 @@ its_cmd_send(struct gic_v3_its_softc *sc, struct its_cmd_desc *desc)
its_cmd_sync(sc, cmd);
if (target != ITS_TARGET_NONE) {
mtx_lock_spin(&sc->its_spin_mtx);
cmd_sync = its_cmd_alloc_locked(sc);
mtx_unlock_spin(&sc->its_spin_mtx);
if (cmd_sync == NULL)
goto end;
desc_sync.cmd_type = ITS_CMD_SYNC;
@ -1341,12 +1339,12 @@ its_cmd_send(struct gic_v3_its_softc *sc, struct its_cmd_desc *desc)
}
end:
/* Update GITS_CWRITER */
mtx_lock_spin(&sc->its_spin_mtx);
cwriter = its_cmd_cwriter_offset(sc, sc->its_cmdq_write);
gic_its_write(sc, 8, GITS_CWRITER, cwriter);
cmd_write = sc->its_cmdq_write;
mtx_unlock_spin(&sc->its_spin_mtx);
its_cmd_wait_completion(sc, cmd, sc->its_cmdq_write);
its_cmd_wait_completion(sc, cmd, cmd_write);
return (0);
}