From 96b418c960af0d5c7185ff5c4af9376eb37ac9d3 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 9 May 2007 09:02:57 +0200 Subject: Add sg helpers for iterating over a scatterlist table First step to being able to change the scatterlist setup without having to modify drivers (a lot :-) Signed-off-by: Jens Axboe --- include/linux/scatterlist.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 4efbd9c445f..bed5ab4d756 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -20,4 +20,13 @@ static inline void sg_init_one(struct scatterlist *sg, const void *buf, sg_set_buf(sg, buf, buflen); } +#define sg_next(sg) ((sg) + 1) +#define sg_last(sg, nents) (&(sg[(nents) - 1])) + +/* + * Loop over each sg element, following the pointer to a new list if necessary + */ +#define for_each_sg(sglist, sg, nr, __i) \ + for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) + #endif /* _LINUX_SCATTERLIST_H */ -- cgit v1.2.3-70-g09d2 From 70eb8040dc81212c884a464b75e37dca8014f3ad Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 16 Jul 2007 21:17:16 +0200 Subject: Add chained sg support to linux/scatterlist.h The core of the patch - allow the last sg element in a scatterlist table to point to the start of a new table. We overload the LSB of the page pointer to indicate whether this is a valid sg entry, or merely a link to the next list. Includes a fix from Bartlomiej Zolnierkiewicz correcting the ifdef ARCH_HAS_SG_CHAIN guarding sg_last(). Signed-off-by: Jens Axboe --- include/linux/scatterlist.h | 79 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index bed5ab4d756..2dc7464cce5 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -20,8 +20,36 @@ static inline void sg_init_one(struct scatterlist *sg, const void *buf, sg_set_buf(sg, buf, buflen); } -#define sg_next(sg) ((sg) + 1) -#define sg_last(sg, nents) (&(sg[(nents) - 1])) +/* + * We overload the LSB of the page pointer to indicate whether it's + * a valid sg entry, or whether it points to the start of a new scatterlist. + * Those low bits are there for everyone! (thanks mason :-) + */ +#define sg_is_chain(sg) ((unsigned long) (sg)->page & 0x01) +#define sg_chain_ptr(sg) \ + ((struct scatterlist *) ((unsigned long) (sg)->page & ~0x01)) + +/** + * sg_next - return the next scatterlist entry in a list + * @sg: The current sg entry + * + * Usually the next entry will be @sg@ + 1, but if this sg element is part + * of a chained scatterlist, it could jump to the start of a new + * scatterlist array. + * + * Note that the caller must ensure that there are further entries after + * the current entry, this function will NOT return NULL for an end-of-list. + * + */ +static inline struct scatterlist *sg_next(struct scatterlist *sg) +{ + sg++; + + if (unlikely(sg_is_chain(sg))) + sg = sg_chain_ptr(sg); + + return sg; +} /* * Loop over each sg element, following the pointer to a new list if necessary @@ -29,4 +57,51 @@ static inline void sg_init_one(struct scatterlist *sg, const void *buf, #define for_each_sg(sglist, sg, nr, __i) \ for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) +/** + * sg_last - return the last scatterlist entry in a list + * @sgl: First entry in the scatterlist + * @nents: Number of entries in the scatterlist + * + * Should only be used casually, it (currently) scan the entire list + * to get the last entry. + * + * Note that the @sgl@ pointer passed in need not be the first one, + * the important bit is that @nents@ denotes the number of entries that + * exist from @sgl@. + * + */ +static inline struct scatterlist *sg_last(struct scatterlist *sgl, + unsigned int nents) +{ +#ifndef ARCH_HAS_SG_CHAIN + struct scatterlist *ret = &sgl[nents - 1]; +#else + struct scatterlist *sg, *ret = NULL; + int i; + + for_each_sg(sgl, sg, nents, i) + ret = sg; + +#endif + return ret; +} + +/** + * sg_chain - Chain two sglists together + * @prv: First scatterlist + * @prv_nents: Number of entries in prv + * @sgl: Second scatterlist + * + * Links @prv@ and @sgl@ together, to form a longer scatterlist. + * + */ +static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, + struct scatterlist *sgl) +{ +#ifndef ARCH_HAS_SG_CHAIN + BUG(); +#endif + prv[prv_nents - 1].page = (struct page *) ((unsigned long) sgl | 0x01); +} + #endif /* _LINUX_SCATTERLIST_H */ -- cgit v1.2.3-70-g09d2 From 8726021626780a73e795c9b939e1ee49ac8c9136 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 16 Oct 2007 11:14:12 +0200 Subject: libata: convert to using sg helpers This converts libata to using the sg helpers for looking up sg elements, instead of doing it manually. Signed-off-by: Jens Axboe --- drivers/ata/libata-core.c | 30 ++++++++++++++++-------------- include/linux/libata.h | 16 ++++++++++------ 2 files changed, 26 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 68699b3e799..e52c33180e4 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1410,7 +1410,7 @@ static void ata_qc_complete_internal(struct ata_queued_cmd *qc) */ unsigned ata_exec_internal_sg(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, - int dma_dir, struct scatterlist *sg, + int dma_dir, struct scatterlist *sgl, unsigned int n_elem, unsigned long timeout) { struct ata_link *link = dev->link; @@ -1472,11 +1472,12 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, qc->dma_dir = dma_dir; if (dma_dir != DMA_NONE) { unsigned int i, buflen = 0; + struct scatterlist *sg; - for (i = 0; i < n_elem; i++) - buflen += sg[i].length; + for_each_sg(sgl, sg, n_elem, i) + buflen += sg->length; - ata_sg_init(qc, sg, n_elem); + ata_sg_init(qc, sgl, n_elem); qc->nbytes = buflen; } @@ -4292,7 +4293,7 @@ void ata_sg_clean(struct ata_queued_cmd *qc) if (qc->n_elem) dma_unmap_sg(ap->dev, sg, qc->n_elem, dir); /* restore last sg */ - sg[qc->orig_n_elem - 1].length += qc->pad_len; + sg_last(sg, qc->orig_n_elem)->length += qc->pad_len; if (pad_buf) { struct scatterlist *psg = &qc->pad_sgent; void *addr = kmap_atomic(psg->page, KM_IRQ0); @@ -4547,6 +4548,7 @@ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen) qc->orig_n_elem = 1; qc->buf_virt = buf; qc->nbytes = buflen; + qc->cursg = qc->__sg; sg_init_one(&qc->sgent, buf, buflen); } @@ -4572,6 +4574,7 @@ void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, qc->__sg = sg; qc->n_elem = n_elem; qc->orig_n_elem = n_elem; + qc->cursg = qc->__sg; } /** @@ -4661,7 +4664,7 @@ static int ata_sg_setup(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; struct scatterlist *sg = qc->__sg; - struct scatterlist *lsg = &sg[qc->n_elem - 1]; + struct scatterlist *lsg = sg_last(qc->__sg, qc->n_elem); int n_elem, pre_n_elem, dir, trim_sg = 0; VPRINTK("ENTER, ata%u\n", ap->print_id); @@ -4825,7 +4828,6 @@ void ata_data_xfer_noirq(struct ata_device *adev, unsigned char *buf, static void ata_pio_sector(struct ata_queued_cmd *qc) { int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); - struct scatterlist *sg = qc->__sg; struct ata_port *ap = qc->ap; struct page *page; unsigned int offset; @@ -4834,8 +4836,8 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) if (qc->curbytes == qc->nbytes - qc->sect_size) ap->hsm_task_state = HSM_ST_LAST; - page = sg[qc->cursg].page; - offset = sg[qc->cursg].offset + qc->cursg_ofs; + page = qc->cursg->page; + offset = qc->cursg->offset + qc->cursg_ofs; /* get the current page and offset */ page = nth_page(page, (offset >> PAGE_SHIFT)); @@ -4863,8 +4865,8 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) qc->curbytes += qc->sect_size; qc->cursg_ofs += qc->sect_size; - if (qc->cursg_ofs == (&sg[qc->cursg])->length) { - qc->cursg++; + if (qc->cursg_ofs == qc->cursg->length) { + qc->cursg = sg_next(qc->cursg); qc->cursg_ofs = 0; } } @@ -4959,7 +4961,7 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) ap->hsm_task_state = HSM_ST_LAST; next_sg: - if (unlikely(qc->cursg >= qc->n_elem)) { + if (unlikely(qc->cursg == sg_last(qc->__sg, qc->n_elem))) { /* * The end of qc->sg is reached and the device expects * more data to transfer. In order not to overrun qc->sg @@ -4982,7 +4984,7 @@ next_sg: return; } - sg = &qc->__sg[qc->cursg]; + sg = qc->cursg; page = sg->page; offset = sg->offset + qc->cursg_ofs; @@ -5021,7 +5023,7 @@ next_sg: qc->cursg_ofs += count; if (qc->cursg_ofs == sg->length) { - qc->cursg++; + qc->cursg = sg_next(qc->cursg); qc->cursg_ofs = 0; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 229a9ff9f92..377e6d4d9be 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -416,6 +416,7 @@ struct ata_queued_cmd { unsigned long flags; /* ATA_QCFLAG_xxx */ unsigned int tag; unsigned int n_elem; + unsigned int n_iter; unsigned int orig_n_elem; int dma_dir; @@ -426,7 +427,7 @@ struct ata_queued_cmd { unsigned int nbytes; unsigned int curbytes; - unsigned int cursg; + struct scatterlist *cursg; unsigned int cursg_ofs; struct scatterlist sgent; @@ -1043,7 +1044,7 @@ ata_sg_is_last(struct scatterlist *sg, struct ata_queued_cmd *qc) return 1; if (qc->pad_len) return 0; - if (((sg - qc->__sg) + 1) == qc->n_elem) + if (qc->n_iter == qc->n_elem) return 1; return 0; } @@ -1051,6 +1052,7 @@ ata_sg_is_last(struct scatterlist *sg, struct ata_queued_cmd *qc) static inline struct scatterlist * ata_qc_first_sg(struct ata_queued_cmd *qc) { + qc->n_iter = 0; if (qc->n_elem) return qc->__sg; if (qc->pad_len) @@ -1063,8 +1065,8 @@ ata_qc_next_sg(struct scatterlist *sg, struct ata_queued_cmd *qc) { if (sg == &qc->pad_sgent) return NULL; - if (++sg - qc->__sg < qc->n_elem) - return sg; + if (++qc->n_iter < qc->n_elem) + return sg_next(sg); if (qc->pad_len) return &qc->pad_sgent; return NULL; @@ -1309,9 +1311,11 @@ static inline void ata_qc_reinit(struct ata_queued_cmd *qc) qc->dma_dir = DMA_NONE; qc->__sg = NULL; qc->flags = 0; - qc->cursg = qc->cursg_ofs = 0; + qc->cursg = NULL; + qc->cursg_ofs = 0; qc->nbytes = qc->curbytes = 0; qc->n_elem = 0; + qc->n_iter = 0; qc->err_mask = 0; qc->pad_len = 0; qc->sect_size = ATA_SECT_SIZE; -- cgit v1.2.3-70-g09d2 From ba2da2f8d61a9d2e24754c6311a4ab6a5e70060a Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 24 Jul 2007 14:42:11 +0200 Subject: i2o: sg chaining support Acked-by: Alan Cox Signed-off-by: Jens Axboe --- include/linux/i2o.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/i2o.h b/include/linux/i2o.h index 9752307d16b..7da5b98d90e 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -32,6 +32,7 @@ #include /* work_struct */ #include #include +#include #include #include /* Needed for MUTEX init macros */ @@ -837,7 +838,7 @@ static inline int i2o_dma_map_sg(struct i2o_controller *c, if ((sizeof(dma_addr_t) > 4) && c->pae_support) *mptr++ = cpu_to_le32(i2o_dma_high(sg_dma_address(sg))); #endif - sg++; + sg = sg_next(sg); } *sg_ptr = mptr; -- cgit v1.2.3-70-g09d2 From 55c16a70041ba55e235c5944dccb9c1de0dd3ca6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 25 Jul 2007 08:13:56 +0200 Subject: IDE: sg chaining support Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Jens Axboe --- drivers/ide/cris/ide-cris.c | 3 ++- drivers/ide/ide-dma.c | 2 +- drivers/ide/ide-io.c | 3 ++- drivers/ide/ide-probe.c | 2 +- drivers/ide/ide-taskfile.c | 18 ++++++++++++++---- drivers/ide/mips/au1xxx-ide.c | 2 +- drivers/ide/pci/sgiioc4.c | 3 ++- drivers/ide/ppc/pmac.c | 2 +- include/linux/ide.h | 2 +- 9 files changed, 25 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/cris/ide-cris.c b/drivers/ide/cris/ide-cris.c index 2b4d2a0ae5c..c306c9f534a 100644 --- a/drivers/ide/cris/ide-cris.c +++ b/drivers/ide/cris/ide-cris.c @@ -939,7 +939,8 @@ static int cris_ide_build_dmatable (ide_drive_t *drive) /* group sequential buffers into one large buffer */ addr = page_to_phys(sg->page) + sg->offset; size = sg_dma_len(sg); - while (sg++, --i) { + while (--i) { + sg = sg_next(sg); if ((addr + size) != page_to_phys(sg->page) + sg->offset) break; size += sg_dma_len(sg); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index b453211ee0f..a4cbbbaccde 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -280,7 +280,7 @@ int ide_build_dmatable (ide_drive_t *drive, struct request *rq) } } - sg++; + sg = sg_next(sg); i--; } diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index f36ff5962af..04273d3c147 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -846,7 +846,8 @@ void ide_init_sg_cmd(ide_drive_t *drive, struct request *rq) ide_hwif_t *hwif = drive->hwif; hwif->nsect = hwif->nleft = rq->nr_sectors; - hwif->cursg = hwif->cursg_ofs = 0; + hwif->cursg_ofs = 0; + hwif->cursg = NULL; } EXPORT_SYMBOL_GPL(ide_init_sg_cmd); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index d1011712601..34b1fb65bc7 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1349,7 +1349,7 @@ static int hwif_init(ide_hwif_t *hwif) if (!hwif->sg_max_nents) hwif->sg_max_nents = PRD_ENTRIES; - hwif->sg_table = kmalloc(sizeof(struct scatterlist)*hwif->sg_max_nents, + hwif->sg_table = kzalloc(sizeof(struct scatterlist)*hwif->sg_max_nents, GFP_KERNEL); if (!hwif->sg_table) { printk(KERN_ERR "%s: unable to allocate SG table.\n", hwif->name); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index aa06dafb74a..2a3c8d49834 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -263,6 +264,7 @@ static void ide_pio_sector(ide_drive_t *drive, unsigned int write) { ide_hwif_t *hwif = drive->hwif; struct scatterlist *sg = hwif->sg_table; + struct scatterlist *cursg = hwif->cursg; struct page *page; #ifdef CONFIG_HIGHMEM unsigned long flags; @@ -270,8 +272,14 @@ static void ide_pio_sector(ide_drive_t *drive, unsigned int write) unsigned int offset; u8 *buf; - page = sg[hwif->cursg].page; - offset = sg[hwif->cursg].offset + hwif->cursg_ofs * SECTOR_SIZE; + cursg = hwif->cursg; + if (!cursg) { + cursg = sg; + hwif->cursg = sg; + } + + page = cursg->page; + offset = cursg->offset + hwif->cursg_ofs * SECTOR_SIZE; /* get the current page and offset */ page = nth_page(page, (offset >> PAGE_SHIFT)); @@ -285,8 +293,8 @@ static void ide_pio_sector(ide_drive_t *drive, unsigned int write) hwif->nleft--; hwif->cursg_ofs++; - if ((hwif->cursg_ofs * SECTOR_SIZE) == sg[hwif->cursg].length) { - hwif->cursg++; + if ((hwif->cursg_ofs * SECTOR_SIZE) == cursg->length) { + hwif->cursg = sg_next(hwif->cursg); hwif->cursg_ofs = 0; } @@ -367,6 +375,8 @@ static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq, static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) { + HWIF(drive)->cursg = NULL; + if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { ide_task_t *task = rq->special; diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index aebde49365d..892d08f61dc 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -296,7 +296,7 @@ static int auide_build_dmatable(ide_drive_t *drive) cur_addr += tc; cur_len -= tc; } - sg++; + sg = sg_next(sg); i--; } diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 85ffaaa39b1..c74fef6bbc9 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -537,7 +538,7 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir) } } - sg++; + sg = sg_next(sg); i--; } diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 7d8873839e2..9e86406bf44 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1539,7 +1539,7 @@ pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq) cur_len -= tc; ++table; } - sg++; + sg = sg_next(sg); i--; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 234fa3df24f..30a1931466a 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -772,7 +772,7 @@ typedef struct hwif_s { unsigned int nsect; unsigned int nleft; - unsigned int cursg; + struct scatterlist *cursg; unsigned int cursg_ofs; int rqsize; /* max sectors per request */ -- cgit v1.2.3-70-g09d2