mmc: msm_sdcc: Use sg_miter API for PIO
Use sgl mapping iterator to process sgls in PIO mode instead
of pointer arithmetic. This prevents possible page faults
caused by accessing kmapped memory when sge buffer is larger
than PAGE_SIZE or straddles several pages. The buffer returned
by kmap_atomic() is only one PAGE_SIZE large, and an sge buffer
larger than a PAGE_SIZE must be processed in PAGE_SIZEd chunks.
Change-Id: I59f130ca2963b5327a4ce7d3016cc558dcb87218
Signed-off-by: Oluwafemi Adeyemi <aadeyemi@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 3ae639e..1ecf2f3 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1042,8 +1042,6 @@
host->curr.got_dataend = 0;
host->curr.got_auto_prog_done = 0;
- memset(&host->pio, 0, sizeof(host->pio));
-
datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
if (host->curr.wait_for_auto_prog_done)
@@ -1078,17 +1076,21 @@
/* Is data transfer in PIO mode required? */
if (!(datactrl & MCI_DPSM_DMAENABLE)) {
- host->pio.sg = data->sg;
- host->pio.sg_len = data->sg_len;
- host->pio.sg_off = 0;
+ unsigned int sg_miter_flags = SG_MITER_ATOMIC;
if (data->flags & MMC_DATA_READ) {
+ sg_miter_flags |= SG_MITER_TO_SG;
pio_irqmask = MCI_RXFIFOHALFFULLMASK;
if (host->curr.xfer_remain < MCI_FIFOSIZE)
pio_irqmask |= MCI_RXDATAAVLBLMASK;
- } else
+ } else {
+ sg_miter_flags |= SG_MITER_FROM_SG;
pio_irqmask = MCI_TXFIFOHALFEMPTYMASK |
MCI_TXFIFOEMPTYMASK;
+ }
+
+ sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
+ sg_miter_flags);
}
if (data->flags & MMC_DATA_READ)
@@ -1254,6 +1256,7 @@
struct msmsdcc_host *host = dev_id;
void __iomem *base = host->base;
uint32_t status;
+ unsigned long flags;
spin_lock(&host->lock);
@@ -1268,9 +1271,10 @@
#if IRQ_DEBUG
msmsdcc_print_status(host, "irq1-r", status);
#endif
+ local_irq_save(flags);
- do {
- unsigned long flags;
+ while (sg_miter_next(&host->sg_miter)) {
+
unsigned int remain, len;
char *buffer;
@@ -1278,12 +1282,8 @@
| MCI_RXDATAAVLBL)))
break;
- /* Map the current scatter buffer */
- local_irq_save(flags);
- buffer = kmap_atomic(sg_page(host->pio.sg),
- KM_BIO_SRC_IRQ) + host->pio.sg->offset;
- buffer += host->pio.sg_off;
- remain = host->pio.sg->length - host->pio.sg_off;
+ buffer = host->sg_miter.addr;
+ remain = host->sg_miter.length;
len = 0;
if (status & MCI_RXACTIVE)
@@ -1294,11 +1294,7 @@
if (len > remain)
len = remain;
- /* Unmap the buffer */
- kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
- local_irq_restore(flags);
-
- host->pio.sg_off += len;
+ host->sg_miter.consumed = len;
host->curr.xfer_remain -= len;
host->curr.data_xfered += len;
remain -= len;
@@ -1306,20 +1302,11 @@
if (remain) /* Done with this page? */
break; /* Nope */
- if (status & MCI_RXACTIVE && host->curr.user_pages)
- flush_dcache_page(sg_page(host->pio.sg));
-
- if (!--host->pio.sg_len) {
- memset(&host->pio, 0, sizeof(host->pio));
- break;
- }
-
- /* Advance to next sg */
- host->pio.sg++;
- host->pio.sg_off = 0;
-
status = readl_relaxed(base + MMCISTATUS);
- } while (1);
+ }
+
+ sg_miter_stop(&host->sg_miter);
+ local_irq_restore(flags);
if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE) {
writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &