On Tue, Apr 29, 2008 at 6:47 AM, Alan Cox <alan@xxxxxxxxxxxxxxxxxxx> wrote: > Right now with PIO transfers we can jam the box up horribly as lock IRQs > off when the transfer is from high memory. We still have other things we > need to fix in this area to really make a difference but this is the > buffering side of the fix (the state machine enable/disable irq stuff is > also needed) > > Currently uses kmalloc - which seems to work best, but trivial to > allocate at init time if wanted > > Signed-off-by: Alan Cox <alan@xxxxxxxxxx> > > diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla-2.6.25-mm1/drivers/ata/libata-sff.c linux-2.6.25-mm1/drivers/ata/libata-sff.c > --- linux.vanilla-2.6.25-mm1/drivers/ata/libata-sff.c 2008-04-28 11:36:48.000000000 +0100 > +++ linux-2.6.25-mm1/drivers/ata/libata-sff.c 2008-04-28 11:42:05.000000000 +0100 > @@ -665,6 +665,62 @@ > } > > /** > + * ata_bounce_pio_xfer - Transfer a block by PIO from high > + * @dev: target device > + * @page: highmem page > + * @offset: offset in page > + * @count: bytes to transfer > + * @do_write: writing if set, reading if not > + * > + * Transfer a page of high memory via PIO. Whenever possible use a bounce > + * buffer to avoid transfers occuring with local IRQ disable > + */ > + > +static unsigned int ata_bounce_pio_xfer(struct ata_device *dev, struct page *page, > + unsigned int offset, int count, int rw) > +{ > + struct ata_port *ap = dev->link->ap; > + unsigned long flags; > + unsigned char *zebedee; > + unsigned char *buf; > + unsigned int consumed; > + > + BUG_ON(offset + count > PAGE_SIZE); > + > + zebedee = kmalloc(count, GFP_ATOMIC); > + if (likely(zebedee)) { > + if (rw == WRITE) { > + local_irq_save(flags); > + buf = kmap_atomic(page, KM_IRQ0); > + memcpy(zebedee, buf + offset, count); > + kunmap_atomic(buf, KM_IRQ0); > + local_irq_restore(flags); > + } > + /* do the actual data transfer */ > + consumed = ap->ops->sff_data_xfer(dev, zebedee, count, rw); > + if (rw == READ) { > + /* Read so bounce data upwards */ > + local_irq_save(flags); > + buf = kmap_atomic(page, KM_IRQ0); > + memcpy(buf + offset, zebedee, count); > + kunmap_atomic(buf, KM_IRQ0); > + local_irq_restore(flags); > + } > + kfree(zebedee); > + } else { > + /* Only used when we are out of buffer memory > + as a last last resort */ > + local_irq_save(flags); > + buf = kmap_atomic(page, KM_IRQ0); > + /* do the actual data transfer */ > + consumed = ap->ops->sff_data_xfer(dev, buf + offset, count, rw); > + kunmap_atomic(buf, KM_IRQ0); > + local_irq_restore(flags); > + } > + return consumed; > +} > + > +/** > * ata_pio_sector - Transfer a sector of data. > * @qc: Command on going > * > @@ -675,7 +731,7 @@ > */ > static void ata_pio_sector(struct ata_queued_cmd *qc) > { > - int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); > + int do_write = (qc->tf.flags & ATA_TFLAG_WRITE) ? WRITE : READ; > struct ata_port *ap = qc->ap; > struct page *page; > unsigned int offset; > @@ -693,19 +749,9 @@ > > DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); > > - if (PageHighMem(page)) { > - unsigned long flags; > - > - /* FIXME: use a bounce buffer */ > - local_irq_save(flags); > - buf = kmap_atomic(page, KM_IRQ0); > - > - /* do the actual data transfer */ > - ap->ops->sff_data_xfer(qc->dev, buf + offset, qc->sect_size, > - do_write); > - > - kunmap_atomic(buf, KM_IRQ0); > - local_irq_restore(flags); > + if (PageHighMem(page) || 1 /* TEST FIXME */) { Did you want the "|| 1" code committed as well? thanks, grant > + ata_bounce_pio_xfer(qc->dev, page, offset, qc->sect_size, > + do_write); > } else { > buf = page_address(page); > ap->ops->sff_data_xfer(qc->dev, buf + offset, qc->sect_size, > @@ -830,19 +876,9 @@ > > DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); > > - if (PageHighMem(page)) { > - unsigned long flags; > - > - /* FIXME: use bounce buffer */ > - local_irq_save(flags); > - buf = kmap_atomic(page, KM_IRQ0); > - > - /* do the actual data transfer */ > - consumed = ap->ops->sff_data_xfer(dev, buf + offset, count, rw); > - > - kunmap_atomic(buf, KM_IRQ0); > - local_irq_restore(flags); > - } else { > + if (PageHighMem(page)) > + consumed = ata_bounce_pio_xfer(qc->dev, page, offset, count, rw); > + else { > buf = page_address(page); > consumed = ap->ops->sff_data_xfer(dev, buf + offset, count, rw); > } > -- > To unsubscribe from this list: send the line "unsubscribe linux-ide" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html