aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Hartland <smh@FreeBSD.org>2014-09-27 19:14:22 +0000
committerSteven Hartland <smh@FreeBSD.org>2014-09-27 19:14:22 +0000
commit6e9a599b2709c19baaec6377e4868a96074fbb8a (patch)
tree5f3362a9f382fbef9dff1a02ee4fd5eef10479ee
parente26e6373c8ae126d763e7e1633d0f8fa7b7eadda (diff)
downloadsrc-6e9a599b2709c19baaec6377e4868a96074fbb8a.tar.gz
src-6e9a599b2709c19baaec6377e4868a96074fbb8a.zip
Use a local STAILQ for unlocked done CCB processing in ahci direct mode
Previously it was possible for issues e.g. use after free, to result from processing the done queue while not holding the channel lock. While this should never happen in practice, unexpected code flows which result in two threads processing from the same queue may be possible. We now use a local STAILQ to prevent this ever being an issue. Sponsored by: Multiplay
Notes
Notes: svn path=/head/; revision=272223
-rw-r--r--sys/dev/ahci/ahci.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/sys/dev/ahci/ahci.c b/sys/dev/ahci/ahci.c
index 0c3197a3f7d3..69fa76bfab42 100644
--- a/sys/dev/ahci/ahci.c
+++ b/sys/dev/ahci/ahci.c
@@ -1126,6 +1126,7 @@ ahci_ch_intr_direct(void *arg)
struct ahci_channel *ch = (struct ahci_channel *)arg;
struct ccb_hdr *ccb_h;
uint32_t istatus;
+ STAILQ_HEAD(, ccb_hdr) tmp_doneq = STAILQ_HEAD_INITIALIZER(tmp_doneq);
/* Read interrupt statuses. */
istatus = ATA_INL(ch->r_mem, AHCI_P_IS);
@@ -1136,9 +1137,14 @@ ahci_ch_intr_direct(void *arg)
ch->batch = 1;
ahci_ch_intr_main(ch, istatus);
ch->batch = 0;
+ /*
+ * Prevent the possibility of issues caused by processing the queue
+ * while unlocked below by moving the contents to a local queue.
+ */
+ STAILQ_CONCAT(&tmp_doneq, &ch->doneq);
mtx_unlock(&ch->mtx);
- while ((ccb_h = STAILQ_FIRST(&ch->doneq)) != NULL) {
- STAILQ_REMOVE_HEAD(&ch->doneq, sim_links.stqe);
+ while ((ccb_h = STAILQ_FIRST(&tmp_doneq)) != NULL) {
+ STAILQ_REMOVE_HEAD(&tmp_doneq, sim_links.stqe);
xpt_done_direct((union ccb *)ccb_h);
}
}