* Re: [PATCH 0/5] sg_ring for scsi
@ 2007-12-20 13:55 ` Boaz Harrosh
0 siblings, 0 replies; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 13:55 UTC (permalink / raw)
To: Jens Axboe, Jens Axboe, James Bottomley, Andrew Morton
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
On Thu, Dec 20 2007 at 9:58 +0200, Jens Axboe <jens.axboe@oracle.com> wrote:
> On Thu, Dec 20 2007, Rusty Russell wrote:
>> On Thursday 20 December 2007 18:07:41 FUJITA Tomonori wrote:
>>> On Thu, 20 Dec 2007 16:45:18 +1100
>>>
>>> Rusty Russell <rusty@rustcorp.com.au> wrote:
>>>> OK, some fixes since last time, as I wade through more SCSI drivers.
>>>> Some drivers use "use_sg" as a flag to know whether the request_buffer is
>>>> a scatterlist: I don't need the counter, but I still need the flag, so I
>>>> fixed that in a more intuitive way (an explicit ->sg pointer in the cmd).
>>> use_sg and the request_buffer will be removed shortly.
>>>
>>> http://marc.info/?l=linux-scsi&m=119754650614813&w=2
>> Thanks! Is there a git tree somewhere with these changes?
>>
>>> I think that we tried the similar idea before, scsi_sgtable, but we
>>> seem to settle in the current simple approach.
>> Yes, a scsi-specific solution is a bad idea: other people use sg.
>> Manipulating the magic chains is horrible; it looks simple to the places
>> which simply want to iterate through it, but it's awful for code which wants
>> to create them.
>
> The current code looks like that to minimize impact on 2.6.24, see this
> branch:
>
> http://git.kernel.dk/?p=linux-2.6-block.git;a=shortlog;h=sg
>
> for how it folds into lib/sg.c and the magic disappears from SCSI.
> Rusty, nobody claimed the sg code in 2.6.24 is perfect. I like to get
> things incrementally there.
>
Dear Jens.
Is this code scheduled for 2.6.25? I cannot find it in mm tree.
Because above code conflicts with the scsi_data_buffer patch,
that is in mm tree and I hope will get accepted into 2.6.25.
Now the concepts are not at all conflicting, only that they
both bang in the same places.
(And by the way it does not apply on scsi-misc either.
And it did not compile in your tree, a missing bit in
ide-scsi.c)
I have rebase and fixed your code on top of scsi_data_buffer patchset.
Please review. (Patchset posted as reply to this mail)
They are totaly untested, based on -mm tree.
We should decide the order of these patches and rebase
accordingly.
AND ...
Please send, to-be-included-in-next-kernel patches to Morton.
This way we can account for them. Also I do not see Ack-by:
of the scsi maintainer in the scsi bits of your patches.
Is it not a costume to Ack-on bits that belong to other maintainers,
even for maintainers?
Boaz
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 1/3] SG: Move functions to lib/scatterlist.c and add sg chaining allocator helpers
2007-12-20 13:55 ` Boaz Harrosh
@ 2007-12-20 14:00 ` Boaz Harrosh
-1 siblings, 0 replies; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 14:00 UTC (permalink / raw)
To: Jens Axboe
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
Manually doing chained sg lists is not trivial, so add some helpers
to make sure that drivers get it right.
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
---
include/linux/scatterlist.h | 125 ++++---------------
lib/Makefile | 2 +-
lib/scatterlist.c | 281 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 307 insertions(+), 101 deletions(-)
create mode 100644 lib/scatterlist.c
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index 416e000..c3ca848 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -7,6 +7,12 @@
#include <linux/string.h>
#include <asm/io.h>
+struct sg_table {
+ struct scatterlist *sgl; /* the list */
+ unsigned int nents; /* number of mapped entries */
+ unsigned int orig_nents; /* original size of list */
+};
+
/*
* Notes on SG table design.
*
@@ -106,31 +112,6 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
}
-/**
- * sg_next - return the next scatterlist entry in a list
- * @sg: The current sg entry
- *
- * Description:
- * 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.
- *
- **/
-static inline struct scatterlist *sg_next(struct scatterlist *sg)
-{
-#ifdef CONFIG_DEBUG_SG
- BUG_ON(sg->sg_magic != SG_MAGIC);
-#endif
- if (sg_is_last(sg))
- return NULL;
-
- 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
*/
@@ -138,40 +119,6 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg)
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
- *
- * Description:
- * 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;
- unsigned int i;
-
- for_each_sg(sgl, sg, nents, i)
- ret = sg;
-
-#endif
-#ifdef CONFIG_DEBUG_SG
- BUG_ON(sgl[0].sg_magic != SG_MAGIC);
- BUG_ON(!sg_is_last(ret));
-#endif
- return ret;
-}
-
-/**
* sg_chain - Chain two sglists together
* @prv: First scatterlist
* @prv_nents: Number of entries in prv
@@ -223,47 +170,6 @@ static inline void sg_mark_end(struct scatterlist *sg)
}
/**
- * sg_init_table - Initialize SG table
- * @sgl: The SG table
- * @nents: Number of entries in table
- *
- * Notes:
- * If this is part of a chained sg table, sg_mark_end() should be
- * used only on the last table part.
- *
- **/
-static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
-{
- memset(sgl, 0, sizeof(*sgl) * nents);
-#ifdef CONFIG_DEBUG_SG
- {
- unsigned int i;
- for (i = 0; i < nents; i++)
- sgl[i].sg_magic = SG_MAGIC;
- }
-#endif
- sg_mark_end(&sgl[nents - 1]);
-}
-
-/**
- * sg_init_one - Initialize a single entry sg list
- * @sg: SG entry
- * @buf: Virtual address for IO
- * @buflen: IO length
- *
- * Notes:
- * This should not be used on a single entry that is part of a larger
- * table. Use sg_init_table() for that.
- *
- **/
-static inline void sg_init_one(struct scatterlist *sg, const void *buf,
- unsigned int buflen)
-{
- sg_init_table(sg, 1);
- sg_set_buf(sg, buf, buflen);
-}
-
-/**
* sg_phys - Return physical address of an sg entry
* @sg: SG entry
*
@@ -293,4 +199,23 @@ static inline void *sg_virt(struct scatterlist *sg)
return page_address(sg_page(sg)) + sg->offset;
}
+struct scatterlist *sg_next(struct scatterlist *);
+struct scatterlist *sg_last(struct scatterlist *s, unsigned int);
+void sg_init_table(struct scatterlist *, unsigned int);
+void sg_init_one(struct scatterlist *, const void *, unsigned int);
+
+typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t);
+typedef void (sg_free_fn)(struct scatterlist *, unsigned int);
+
+void __sg_free_table(struct sg_table *, sg_free_fn *);
+void sg_free_table(struct sg_table *);
+int __sg_alloc_table(struct sg_table *, unsigned int, gfp_t, sg_alloc_fn *);
+int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
+
+/*
+ * Maximum number of entries that will be allocated in one piece, if
+ * a list larger than this is required then chaining will be utilized.
+ */
+#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist))
+
#endif /* _LINUX_SCATTERLIST_H */
diff --git a/lib/Makefile b/lib/Makefile
index b6793ed..89841dc 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -6,7 +6,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
rbtree.o radix-tree.o dump_stack.o \
idr.o int_sqrt.o extable.o prio_tree.o \
sha1.o irq_regs.o reciprocal_div.o argv_split.o \
- proportions.o prio_heap.o
+ proportions.o prio_heap.o scatterlist.o
lib-$(CONFIG_MMU) += ioremap.o
lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
new file mode 100644
index 0000000..02aaa27
--- /dev/null
+++ b/lib/scatterlist.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com>
+ *
+ * Scatterlist handling helpers.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+
+/**
+ * sg_next - return the next scatterlist entry in a list
+ * @sg: The current sg entry
+ *
+ * Description:
+ * 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.
+ *
+ **/
+struct scatterlist *sg_next(struct scatterlist *sg)
+{
+#ifdef CONFIG_DEBUG_SG
+ BUG_ON(sg->sg_magic != SG_MAGIC);
+#endif
+ if (sg_is_last(sg))
+ return NULL;
+
+ sg++;
+ if (unlikely(sg_is_chain(sg)))
+ sg = sg_chain_ptr(sg);
+
+ return sg;
+}
+EXPORT_SYMBOL(sg_next);
+
+/**
+ * sg_last - return the last scatterlist entry in a list
+ * @sgl: First entry in the scatterlist
+ * @nents: Number of entries in the scatterlist
+ *
+ * Description:
+ * Should only be used casually, it (currently) scans 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@.
+ *
+ **/
+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;
+ unsigned int i;
+
+ for_each_sg(sgl, sg, nents, i)
+ ret = sg;
+
+#endif
+#ifdef CONFIG_DEBUG_SG
+ BUG_ON(sgl[0].sg_magic != SG_MAGIC);
+ BUG_ON(!sg_is_last(ret));
+#endif
+ return ret;
+}
+EXPORT_SYMBOL(sg_last);
+
+/**
+ * sg_init_table - Initialize SG table
+ * @sgl: The SG table
+ * @nents: Number of entries in table
+ *
+ * Notes:
+ * If this is part of a chained sg table, sg_mark_end() should be
+ * used only on the last table part.
+ *
+ **/
+void sg_init_table(struct scatterlist *sgl, unsigned int nents)
+{
+ memset(sgl, 0, sizeof(*sgl) * nents);
+#ifdef CONFIG_DEBUG_SG
+ {
+ unsigned int i;
+ for (i = 0; i < nents; i++)
+ sgl[i].sg_magic = SG_MAGIC;
+ }
+#endif
+ sg_mark_end(&sgl[nents - 1]);
+}
+EXPORT_SYMBOL(sg_init_table);
+
+/**
+ * sg_init_one - Initialize a single entry sg list
+ * @sg: SG entry
+ * @buf: Virtual address for IO
+ * @buflen: IO length
+ *
+ **/
+void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen)
+{
+ sg_init_table(sg, 1);
+ sg_set_buf(sg, buf, buflen);
+}
+EXPORT_SYMBOL(sg_init_one);
+
+/*
+ * The default behaviour of sg_alloc_table() is to use these kmalloc/kfree
+ * helpers.
+ */
+static struct scatterlist *sg_kmalloc(unsigned int nents, gfp_t gfp_mask)
+{
+ if (nents == SG_MAX_SINGLE_ALLOC)
+ return (struct scatterlist *) __get_free_page(gfp_mask);
+ else
+ return kmalloc(nents * sizeof(struct scatterlist), gfp_mask);
+}
+
+static void sg_kfree(struct scatterlist *sg, unsigned int nents)
+{
+ if (nents == SG_MAX_SINGLE_ALLOC)
+ free_page((unsigned long) sg);
+ else
+ kfree(sg);
+}
+
+/**
+ * __sg_free_table - Free a previously mapped sg table
+ * @table: The sg table header to use
+ * @free_fn: Free function
+ *
+ * Description:
+ * Free an sg table previously allocated and setup with __sg_alloc_table().
+ *
+ **/
+void __sg_free_table(struct sg_table *table, sg_free_fn *free_fn)
+{
+ struct scatterlist *sgl, *next;
+
+ if (unlikely(!table->sgl))
+ return;
+
+ sgl = table->sgl;
+ while (table->orig_nents) {
+ unsigned int alloc_size = table->orig_nents;
+ unsigned int sg_size;
+
+ /*
+ * If we have more than SG_MAX_SINGLE_ALLOC segments left,
+ * then assign 'next' to the sg table after the current one.
+ * sg_size is then one less than alloc size, since the last
+ * element is the chain pointer.
+ */
+ if (alloc_size > SG_MAX_SINGLE_ALLOC) {
+ next = sg_chain_ptr(&sgl[SG_MAX_SINGLE_ALLOC - 1]);
+ alloc_size = SG_MAX_SINGLE_ALLOC;
+ sg_size = alloc_size - 1;
+ } else {
+ sg_size = alloc_size;
+ next = NULL;
+ }
+
+ table->orig_nents -= sg_size;
+ free_fn(sgl, alloc_size);
+ sgl = next;
+ }
+
+ table->sgl = NULL;
+}
+EXPORT_SYMBOL(__sg_free_table);
+
+/**
+ * sg_free_table - Free a previously allocated sg table
+ * @table: The mapped sg table header
+ *
+ **/
+void sg_free_table(struct sg_table *table)
+{
+ __sg_free_table(table, sg_kfree);
+}
+EXPORT_SYMBOL(sg_free_table);
+
+/**
+ * __sg_alloc_table - Allocate and initialize an sg table with given allocator
+ * @table: The sg table header to use
+ * @nents: Number of entries in sg list
+ * @gfp_mask: GFP allocation mask
+ * @alloc_fn: Allocator to use
+ *
+ * Notes:
+ * If this function returns non-0 (eg failure), the caller must call
+ * __sg_free_table() to cleanup any leftover allocations.
+ *
+ **/
+int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask,
+ sg_alloc_fn *alloc_fn)
+{
+ struct scatterlist *sg, *prv;
+ unsigned int left;
+
+#ifndef ARCH_HAS_SG_CHAIN
+ BUG_ON(nents > SG_MAX_SINGLE_ALLOC);
+#endif
+
+ memset(table, 0, sizeof(*table));
+
+ left = nents;
+ prv = NULL;
+ do {
+ unsigned int sg_size, alloc_size = left;
+
+ if (alloc_size > SG_MAX_SINGLE_ALLOC) {
+ alloc_size = SG_MAX_SINGLE_ALLOC;
+ sg_size = alloc_size - 1;
+ } else
+ sg_size = alloc_size;
+
+ left -= sg_size;
+
+ sg = alloc_fn(alloc_size, gfp_mask);
+ if (unlikely(!sg))
+ return -ENOMEM;
+
+ sg_init_table(sg, alloc_size);
+ table->nents = table->orig_nents += sg_size;
+
+ /*
+ * If this is the first mapping, assign the sg table header.
+ * If this is not the first mapping, chain previous part.
+ */
+ if (prv)
+ sg_chain(prv, SG_MAX_SINGLE_ALLOC, sg);
+ else
+ table->sgl = sg;
+
+ /*
+ * If no more entries after this one, mark the end
+ */
+ if (!left)
+ sg_mark_end(&sg[sg_size - 1]);
+
+ /*
+ * only really needed for mempool backed sg allocations (like
+ * SCSI), a possible improvement here would be to pass the
+ * table pointer into the allocator and let that clear these
+ * flags
+ */
+ gfp_mask &= ~__GFP_WAIT;
+ gfp_mask |= __GFP_HIGH;
+ prv = sg;
+ } while (left);
+
+ return 0;
+}
+EXPORT_SYMBOL(__sg_alloc_table);
+
+/**
+ * sg_alloc_table - Allocate and initialize an sg table
+ * @table: The sg table header to use
+ * @nents: Number of entries in sg list
+ * @gfp_mask: GFP allocation mask
+ *
+ * Description:
+ * Allocate and initialize an sg table. If @nents@ is larger than
+ * SG_MAX_SINGLE_ALLOC a chained sg table will be setup.
+ *
+ **/
+int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
+{
+ int ret;
+
+ ret = __sg_alloc_table(table, nents, gfp_mask, sg_kmalloc);
+ if (unlikely(ret))
+ __sg_free_table(table, sg_kfree);
+
+ return ret;
+}
+EXPORT_SYMBOL(sg_alloc_table);
--
1.5.3.3
^ permalink raw reply related [flat|nested] 40+ messages in thread* [PATCH 1/3] SG: Move functions to lib/scatterlist.c and add sg chaining allocator helpers
@ 2007-12-20 14:00 ` Boaz Harrosh
0 siblings, 0 replies; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 14:00 UTC (permalink / raw)
To: Jens Axboe, Jens Axboe, James Bottomley, Andrew Morton
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
Manually doing chained sg lists is not trivial, so add some helpers
to make sure that drivers get it right.
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
---
include/linux/scatterlist.h | 125 ++++---------------
lib/Makefile | 2 +-
lib/scatterlist.c | 281 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 307 insertions(+), 101 deletions(-)
create mode 100644 lib/scatterlist.c
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index 416e000..c3ca848 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -7,6 +7,12 @@
#include <linux/string.h>
#include <asm/io.h>
+struct sg_table {
+ struct scatterlist *sgl; /* the list */
+ unsigned int nents; /* number of mapped entries */
+ unsigned int orig_nents; /* original size of list */
+};
+
/*
* Notes on SG table design.
*
@@ -106,31 +112,6 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
}
-/**
- * sg_next - return the next scatterlist entry in a list
- * @sg: The current sg entry
- *
- * Description:
- * 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.
- *
- **/
-static inline struct scatterlist *sg_next(struct scatterlist *sg)
-{
-#ifdef CONFIG_DEBUG_SG
- BUG_ON(sg->sg_magic != SG_MAGIC);
-#endif
- if (sg_is_last(sg))
- return NULL;
-
- 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
*/
@@ -138,40 +119,6 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg)
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
- *
- * Description:
- * 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;
- unsigned int i;
-
- for_each_sg(sgl, sg, nents, i)
- ret = sg;
-
-#endif
-#ifdef CONFIG_DEBUG_SG
- BUG_ON(sgl[0].sg_magic != SG_MAGIC);
- BUG_ON(!sg_is_last(ret));
-#endif
- return ret;
-}
-
-/**
* sg_chain - Chain two sglists together
* @prv: First scatterlist
* @prv_nents: Number of entries in prv
@@ -223,47 +170,6 @@ static inline void sg_mark_end(struct scatterlist *sg)
}
/**
- * sg_init_table - Initialize SG table
- * @sgl: The SG table
- * @nents: Number of entries in table
- *
- * Notes:
- * If this is part of a chained sg table, sg_mark_end() should be
- * used only on the last table part.
- *
- **/
-static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
-{
- memset(sgl, 0, sizeof(*sgl) * nents);
-#ifdef CONFIG_DEBUG_SG
- {
- unsigned int i;
- for (i = 0; i < nents; i++)
- sgl[i].sg_magic = SG_MAGIC;
- }
-#endif
- sg_mark_end(&sgl[nents - 1]);
-}
-
-/**
- * sg_init_one - Initialize a single entry sg list
- * @sg: SG entry
- * @buf: Virtual address for IO
- * @buflen: IO length
- *
- * Notes:
- * This should not be used on a single entry that is part of a larger
- * table. Use sg_init_table() for that.
- *
- **/
-static inline void sg_init_one(struct scatterlist *sg, const void *buf,
- unsigned int buflen)
-{
- sg_init_table(sg, 1);
- sg_set_buf(sg, buf, buflen);
-}
-
-/**
* sg_phys - Return physical address of an sg entry
* @sg: SG entry
*
@@ -293,4 +199,23 @@ static inline void *sg_virt(struct scatterlist *sg)
return page_address(sg_page(sg)) + sg->offset;
}
+struct scatterlist *sg_next(struct scatterlist *);
+struct scatterlist *sg_last(struct scatterlist *s, unsigned int);
+void sg_init_table(struct scatterlist *, unsigned int);
+void sg_init_one(struct scatterlist *, const void *, unsigned int);
+
+typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t);
+typedef void (sg_free_fn)(struct scatterlist *, unsigned int);
+
+void __sg_free_table(struct sg_table *, sg_free_fn *);
+void sg_free_table(struct sg_table *);
+int __sg_alloc_table(struct sg_table *, unsigned int, gfp_t, sg_alloc_fn *);
+int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
+
+/*
+ * Maximum number of entries that will be allocated in one piece, if
+ * a list larger than this is required then chaining will be utilized.
+ */
+#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist))
+
#endif /* _LINUX_SCATTERLIST_H */
diff --git a/lib/Makefile b/lib/Makefile
index b6793ed..89841dc 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -6,7 +6,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
rbtree.o radix-tree.o dump_stack.o \
idr.o int_sqrt.o extable.o prio_tree.o \
sha1.o irq_regs.o reciprocal_div.o argv_split.o \
- proportions.o prio_heap.o
+ proportions.o prio_heap.o scatterlist.o
lib-$(CONFIG_MMU) += ioremap.o
lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
new file mode 100644
index 0000000..02aaa27
--- /dev/null
+++ b/lib/scatterlist.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com>
+ *
+ * Scatterlist handling helpers.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+
+/**
+ * sg_next - return the next scatterlist entry in a list
+ * @sg: The current sg entry
+ *
+ * Description:
+ * 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.
+ *
+ **/
+struct scatterlist *sg_next(struct scatterlist *sg)
+{
+#ifdef CONFIG_DEBUG_SG
+ BUG_ON(sg->sg_magic != SG_MAGIC);
+#endif
+ if (sg_is_last(sg))
+ return NULL;
+
+ sg++;
+ if (unlikely(sg_is_chain(sg)))
+ sg = sg_chain_ptr(sg);
+
+ return sg;
+}
+EXPORT_SYMBOL(sg_next);
+
+/**
+ * sg_last - return the last scatterlist entry in a list
+ * @sgl: First entry in the scatterlist
+ * @nents: Number of entries in the scatterlist
+ *
+ * Description:
+ * Should only be used casually, it (currently) scans 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@.
+ *
+ **/
+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;
+ unsigned int i;
+
+ for_each_sg(sgl, sg, nents, i)
+ ret = sg;
+
+#endif
+#ifdef CONFIG_DEBUG_SG
+ BUG_ON(sgl[0].sg_magic != SG_MAGIC);
+ BUG_ON(!sg_is_last(ret));
+#endif
+ return ret;
+}
+EXPORT_SYMBOL(sg_last);
+
+/**
+ * sg_init_table - Initialize SG table
+ * @sgl: The SG table
+ * @nents: Number of entries in table
+ *
+ * Notes:
+ * If this is part of a chained sg table, sg_mark_end() should be
+ * used only on the last table part.
+ *
+ **/
+void sg_init_table(struct scatterlist *sgl, unsigned int nents)
+{
+ memset(sgl, 0, sizeof(*sgl) * nents);
+#ifdef CONFIG_DEBUG_SG
+ {
+ unsigned int i;
+ for (i = 0; i < nents; i++)
+ sgl[i].sg_magic = SG_MAGIC;
+ }
+#endif
+ sg_mark_end(&sgl[nents - 1]);
+}
+EXPORT_SYMBOL(sg_init_table);
+
+/**
+ * sg_init_one - Initialize a single entry sg list
+ * @sg: SG entry
+ * @buf: Virtual address for IO
+ * @buflen: IO length
+ *
+ **/
+void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen)
+{
+ sg_init_table(sg, 1);
+ sg_set_buf(sg, buf, buflen);
+}
+EXPORT_SYMBOL(sg_init_one);
+
+/*
+ * The default behaviour of sg_alloc_table() is to use these kmalloc/kfree
+ * helpers.
+ */
+static struct scatterlist *sg_kmalloc(unsigned int nents, gfp_t gfp_mask)
+{
+ if (nents == SG_MAX_SINGLE_ALLOC)
+ return (struct scatterlist *) __get_free_page(gfp_mask);
+ else
+ return kmalloc(nents * sizeof(struct scatterlist), gfp_mask);
+}
+
+static void sg_kfree(struct scatterlist *sg, unsigned int nents)
+{
+ if (nents == SG_MAX_SINGLE_ALLOC)
+ free_page((unsigned long) sg);
+ else
+ kfree(sg);
+}
+
+/**
+ * __sg_free_table - Free a previously mapped sg table
+ * @table: The sg table header to use
+ * @free_fn: Free function
+ *
+ * Description:
+ * Free an sg table previously allocated and setup with __sg_alloc_table().
+ *
+ **/
+void __sg_free_table(struct sg_table *table, sg_free_fn *free_fn)
+{
+ struct scatterlist *sgl, *next;
+
+ if (unlikely(!table->sgl))
+ return;
+
+ sgl = table->sgl;
+ while (table->orig_nents) {
+ unsigned int alloc_size = table->orig_nents;
+ unsigned int sg_size;
+
+ /*
+ * If we have more than SG_MAX_SINGLE_ALLOC segments left,
+ * then assign 'next' to the sg table after the current one.
+ * sg_size is then one less than alloc size, since the last
+ * element is the chain pointer.
+ */
+ if (alloc_size > SG_MAX_SINGLE_ALLOC) {
+ next = sg_chain_ptr(&sgl[SG_MAX_SINGLE_ALLOC - 1]);
+ alloc_size = SG_MAX_SINGLE_ALLOC;
+ sg_size = alloc_size - 1;
+ } else {
+ sg_size = alloc_size;
+ next = NULL;
+ }
+
+ table->orig_nents -= sg_size;
+ free_fn(sgl, alloc_size);
+ sgl = next;
+ }
+
+ table->sgl = NULL;
+}
+EXPORT_SYMBOL(__sg_free_table);
+
+/**
+ * sg_free_table - Free a previously allocated sg table
+ * @table: The mapped sg table header
+ *
+ **/
+void sg_free_table(struct sg_table *table)
+{
+ __sg_free_table(table, sg_kfree);
+}
+EXPORT_SYMBOL(sg_free_table);
+
+/**
+ * __sg_alloc_table - Allocate and initialize an sg table with given allocator
+ * @table: The sg table header to use
+ * @nents: Number of entries in sg list
+ * @gfp_mask: GFP allocation mask
+ * @alloc_fn: Allocator to use
+ *
+ * Notes:
+ * If this function returns non-0 (eg failure), the caller must call
+ * __sg_free_table() to cleanup any leftover allocations.
+ *
+ **/
+int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask,
+ sg_alloc_fn *alloc_fn)
+{
+ struct scatterlist *sg, *prv;
+ unsigned int left;
+
+#ifndef ARCH_HAS_SG_CHAIN
+ BUG_ON(nents > SG_MAX_SINGLE_ALLOC);
+#endif
+
+ memset(table, 0, sizeof(*table));
+
+ left = nents;
+ prv = NULL;
+ do {
+ unsigned int sg_size, alloc_size = left;
+
+ if (alloc_size > SG_MAX_SINGLE_ALLOC) {
+ alloc_size = SG_MAX_SINGLE_ALLOC;
+ sg_size = alloc_size - 1;
+ } else
+ sg_size = alloc_size;
+
+ left -= sg_size;
+
+ sg = alloc_fn(alloc_size, gfp_mask);
+ if (unlikely(!sg))
+ return -ENOMEM;
+
+ sg_init_table(sg, alloc_size);
+ table->nents = table->orig_nents += sg_size;
+
+ /*
+ * If this is the first mapping, assign the sg table header.
+ * If this is not the first mapping, chain previous part.
+ */
+ if (prv)
+ sg_chain(prv, SG_MAX_SINGLE_ALLOC, sg);
+ else
+ table->sgl = sg;
+
+ /*
+ * If no more entries after this one, mark the end
+ */
+ if (!left)
+ sg_mark_end(&sg[sg_size - 1]);
+
+ /*
+ * only really needed for mempool backed sg allocations (like
+ * SCSI), a possible improvement here would be to pass the
+ * table pointer into the allocator and let that clear these
+ * flags
+ */
+ gfp_mask &= ~__GFP_WAIT;
+ gfp_mask |= __GFP_HIGH;
+ prv = sg;
+ } while (left);
+
+ return 0;
+}
+EXPORT_SYMBOL(__sg_alloc_table);
+
+/**
+ * sg_alloc_table - Allocate and initialize an sg table
+ * @table: The sg table header to use
+ * @nents: Number of entries in sg list
+ * @gfp_mask: GFP allocation mask
+ *
+ * Description:
+ * Allocate and initialize an sg table. If @nents@ is larger than
+ * SG_MAX_SINGLE_ALLOC a chained sg table will be setup.
+ *
+ **/
+int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
+{
+ int ret;
+
+ ret = __sg_alloc_table(table, nents, gfp_mask, sg_kmalloc);
+ if (unlikely(ret))
+ __sg_free_table(table, sg_kfree);
+
+ return ret;
+}
+EXPORT_SYMBOL(sg_alloc_table);
--
1.5.3.3
^ permalink raw reply related [flat|nested] 40+ messages in thread* Re: [PATCH 1/3] SG: Move functions to lib/scatterlist.c and add sg chaining allocator helpers
2007-12-20 14:00 ` Boaz Harrosh
@ 2007-12-20 14:21 ` Boaz Harrosh
-1 siblings, 0 replies; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 14:21 UTC (permalink / raw)
To: Jens Axboe
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
A small comment in body
On Thu, Dec 20 2007 at 16:00 +0200, Boaz Harrosh <bharrosh@panasas.com> wrote:
> Manually doing chained sg lists is not trivial, so add some helpers
> to make sure that drivers get it right.
>
> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
> ---
> include/linux/scatterlist.h | 125 ++++---------------
> lib/Makefile | 2 +-
> lib/scatterlist.c | 281 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 307 insertions(+), 101 deletions(-)
> create mode 100644 lib/scatterlist.c
>
> diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
> index 416e000..c3ca848 100644
> --- a/include/linux/scatterlist.h
> +++ b/include/linux/scatterlist.h
> @@ -7,6 +7,12 @@
> #include <linux/string.h>
> #include <asm/io.h>
>
> +struct sg_table {
> + struct scatterlist *sgl; /* the list */
> + unsigned int nents; /* number of mapped entries */
> + unsigned int orig_nents; /* original size of list */
> +};
> +
> /*
> * Notes on SG table design.
> *
> @@ -106,31 +112,6 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
> sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
> }
>
> -/**
> - * sg_next - return the next scatterlist entry in a list
> - * @sg: The current sg entry
> - *
> - * Description:
> - * 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.
> - *
> - **/
> -static inline struct scatterlist *sg_next(struct scatterlist *sg)
> -{
> -#ifdef CONFIG_DEBUG_SG
> - BUG_ON(sg->sg_magic != SG_MAGIC);
> -#endif
> - if (sg_is_last(sg))
> - return NULL;
> -
> - 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
> */
> @@ -138,40 +119,6 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg)
> 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
> - *
> - * Description:
> - * 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;
> - unsigned int i;
> -
> - for_each_sg(sgl, sg, nents, i)
> - ret = sg;
> -
> -#endif
> -#ifdef CONFIG_DEBUG_SG
> - BUG_ON(sgl[0].sg_magic != SG_MAGIC);
> - BUG_ON(!sg_is_last(ret));
> -#endif
> - return ret;
> -}
> -
> -/**
> * sg_chain - Chain two sglists together
> * @prv: First scatterlist
> * @prv_nents: Number of entries in prv
> @@ -223,47 +170,6 @@ static inline void sg_mark_end(struct scatterlist *sg)
> }
>
> /**
> - * sg_init_table - Initialize SG table
> - * @sgl: The SG table
> - * @nents: Number of entries in table
> - *
> - * Notes:
> - * If this is part of a chained sg table, sg_mark_end() should be
> - * used only on the last table part.
> - *
> - **/
> -static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
> -{
> - memset(sgl, 0, sizeof(*sgl) * nents);
> -#ifdef CONFIG_DEBUG_SG
> - {
> - unsigned int i;
> - for (i = 0; i < nents; i++)
> - sgl[i].sg_magic = SG_MAGIC;
> - }
> -#endif
> - sg_mark_end(&sgl[nents - 1]);
> -}
> -
> -/**
> - * sg_init_one - Initialize a single entry sg list
> - * @sg: SG entry
> - * @buf: Virtual address for IO
> - * @buflen: IO length
> - *
> - * Notes:
> - * This should not be used on a single entry that is part of a larger
> - * table. Use sg_init_table() for that.
> - *
> - **/
> -static inline void sg_init_one(struct scatterlist *sg, const void *buf,
> - unsigned int buflen)
> -{
> - sg_init_table(sg, 1);
> - sg_set_buf(sg, buf, buflen);
> -}
> -
> -/**
> * sg_phys - Return physical address of an sg entry
> * @sg: SG entry
> *
> @@ -293,4 +199,23 @@ static inline void *sg_virt(struct scatterlist *sg)
> return page_address(sg_page(sg)) + sg->offset;
> }
>
> +struct scatterlist *sg_next(struct scatterlist *);
I don't like that this became exported. I would prefer if
it could stay inline. if at all possible?
> +struct scatterlist *sg_last(struct scatterlist *s, unsigned int);
> +void sg_init_table(struct scatterlist *, unsigned int);
> +void sg_init_one(struct scatterlist *, const void *, unsigned int);
> +
> +typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t);
> +typedef void (sg_free_fn)(struct scatterlist *, unsigned int);
> +
> +void __sg_free_table(struct sg_table *, sg_free_fn *);
> +void sg_free_table(struct sg_table *);
> +int __sg_alloc_table(struct sg_table *, unsigned int, gfp_t, sg_alloc_fn *);
> +int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
> +
> +/*
> + * Maximum number of entries that will be allocated in one piece, if
> + * a list larger than this is required then chaining will be utilized.
> + */
> +#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist))
Yes this is a well needed calculation.
Only now the SCSI part of the allocator is missing.
What is needed is the code a posted a while back that
automatically adjusts pools and sizes to this constant.
I will post that again soon.
(Note that this is a bug on current code since above is 256
in 32 bit code and scsi_lib is only ready for 128)
> +
> #endif /* _LINUX_SCATTERLIST_H */
> diff --git a/lib/Makefile b/lib/Makefile
> index b6793ed..89841dc 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -6,7 +6,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
> rbtree.o radix-tree.o dump_stack.o \
> idr.o int_sqrt.o extable.o prio_tree.o \
> sha1.o irq_regs.o reciprocal_div.o argv_split.o \
> - proportions.o prio_heap.o
> + proportions.o prio_heap.o scatterlist.o
>
> lib-$(CONFIG_MMU) += ioremap.o
> lib-$(CONFIG_SMP) += cpumask.o
> diff --git a/lib/scatterlist.c b/lib/scatterlist.c
> new file mode 100644
> index 0000000..02aaa27
> --- /dev/null
> +++ b/lib/scatterlist.c
> @@ -0,0 +1,281 @@
> +/*
> + * Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com>
> + *
> + * Scatterlist handling helpers.
> + *
> + * This source code is licensed under the GNU General Public License,
> + * Version 2. See the file COPYING for more details.
> + */
> +#include <linux/module.h>
> +#include <linux/scatterlist.h>
> +
> +/**
> + * sg_next - return the next scatterlist entry in a list
> + * @sg: The current sg entry
> + *
> + * Description:
> + * 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.
> + *
> + **/
> +struct scatterlist *sg_next(struct scatterlist *sg)
> +{
> +#ifdef CONFIG_DEBUG_SG
> + BUG_ON(sg->sg_magic != SG_MAGIC);
> +#endif
> + if (sg_is_last(sg))
> + return NULL;
> +
> + sg++;
> + if (unlikely(sg_is_chain(sg)))
> + sg = sg_chain_ptr(sg);
> +
> + return sg;
> +}
> +EXPORT_SYMBOL(sg_next);
> +
> +/**
> + * sg_last - return the last scatterlist entry in a list
> + * @sgl: First entry in the scatterlist
> + * @nents: Number of entries in the scatterlist
> + *
> + * Description:
> + * Should only be used casually, it (currently) scans 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@.
> + *
> + **/
> +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;
> + unsigned int i;
> +
> + for_each_sg(sgl, sg, nents, i)
> + ret = sg;
> +
> +#endif
> +#ifdef CONFIG_DEBUG_SG
> + BUG_ON(sgl[0].sg_magic != SG_MAGIC);
> + BUG_ON(!sg_is_last(ret));
> +#endif
> + return ret;
> +}
> +EXPORT_SYMBOL(sg_last);
> +
> +/**
> + * sg_init_table - Initialize SG table
> + * @sgl: The SG table
> + * @nents: Number of entries in table
> + *
> + * Notes:
> + * If this is part of a chained sg table, sg_mark_end() should be
> + * used only on the last table part.
> + *
> + **/
> +void sg_init_table(struct scatterlist *sgl, unsigned int nents)
> +{
> + memset(sgl, 0, sizeof(*sgl) * nents);
> +#ifdef CONFIG_DEBUG_SG
> + {
> + unsigned int i;
> + for (i = 0; i < nents; i++)
> + sgl[i].sg_magic = SG_MAGIC;
> + }
> +#endif
> + sg_mark_end(&sgl[nents - 1]);
> +}
> +EXPORT_SYMBOL(sg_init_table);
> +
> +/**
> + * sg_init_one - Initialize a single entry sg list
> + * @sg: SG entry
> + * @buf: Virtual address for IO
> + * @buflen: IO length
> + *
> + **/
> +void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen)
> +{
> + sg_init_table(sg, 1);
> + sg_set_buf(sg, buf, buflen);
> +}
> +EXPORT_SYMBOL(sg_init_one);
> +
> +/*
> + * The default behaviour of sg_alloc_table() is to use these kmalloc/kfree
> + * helpers.
> + */
> +static struct scatterlist *sg_kmalloc(unsigned int nents, gfp_t gfp_mask)
> +{
> + if (nents == SG_MAX_SINGLE_ALLOC)
> + return (struct scatterlist *) __get_free_page(gfp_mask);
> + else
> + return kmalloc(nents * sizeof(struct scatterlist), gfp_mask);
> +}
> +
> +static void sg_kfree(struct scatterlist *sg, unsigned int nents)
> +{
> + if (nents == SG_MAX_SINGLE_ALLOC)
> + free_page((unsigned long) sg);
> + else
> + kfree(sg);
> +}
> +
> +/**
> + * __sg_free_table - Free a previously mapped sg table
> + * @table: The sg table header to use
> + * @free_fn: Free function
> + *
> + * Description:
> + * Free an sg table previously allocated and setup with __sg_alloc_table().
> + *
> + **/
> +void __sg_free_table(struct sg_table *table, sg_free_fn *free_fn)
> +{
> + struct scatterlist *sgl, *next;
> +
> + if (unlikely(!table->sgl))
> + return;
> +
> + sgl = table->sgl;
> + while (table->orig_nents) {
> + unsigned int alloc_size = table->orig_nents;
> + unsigned int sg_size;
> +
> + /*
> + * If we have more than SG_MAX_SINGLE_ALLOC segments left,
> + * then assign 'next' to the sg table after the current one.
> + * sg_size is then one less than alloc size, since the last
> + * element is the chain pointer.
> + */
> + if (alloc_size > SG_MAX_SINGLE_ALLOC) {
> + next = sg_chain_ptr(&sgl[SG_MAX_SINGLE_ALLOC - 1]);
> + alloc_size = SG_MAX_SINGLE_ALLOC;
> + sg_size = alloc_size - 1;
> + } else {
> + sg_size = alloc_size;
> + next = NULL;
> + }
> +
> + table->orig_nents -= sg_size;
> + free_fn(sgl, alloc_size);
> + sgl = next;
> + }
> +
> + table->sgl = NULL;
> +}
> +EXPORT_SYMBOL(__sg_free_table);
> +
> +/**
> + * sg_free_table - Free a previously allocated sg table
> + * @table: The mapped sg table header
> + *
> + **/
> +void sg_free_table(struct sg_table *table)
> +{
> + __sg_free_table(table, sg_kfree);
> +}
> +EXPORT_SYMBOL(sg_free_table);
> +
> +/**
> + * __sg_alloc_table - Allocate and initialize an sg table with given allocator
> + * @table: The sg table header to use
> + * @nents: Number of entries in sg list
> + * @gfp_mask: GFP allocation mask
> + * @alloc_fn: Allocator to use
> + *
> + * Notes:
> + * If this function returns non-0 (eg failure), the caller must call
> + * __sg_free_table() to cleanup any leftover allocations.
> + *
> + **/
> +int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask,
> + sg_alloc_fn *alloc_fn)
> +{
> + struct scatterlist *sg, *prv;
> + unsigned int left;
> +
> +#ifndef ARCH_HAS_SG_CHAIN
> + BUG_ON(nents > SG_MAX_SINGLE_ALLOC);
> +#endif
> +
> + memset(table, 0, sizeof(*table));
> +
> + left = nents;
> + prv = NULL;
> + do {
> + unsigned int sg_size, alloc_size = left;
> +
> + if (alloc_size > SG_MAX_SINGLE_ALLOC) {
> + alloc_size = SG_MAX_SINGLE_ALLOC;
> + sg_size = alloc_size - 1;
> + } else
> + sg_size = alloc_size;
> +
> + left -= sg_size;
> +
> + sg = alloc_fn(alloc_size, gfp_mask);
> + if (unlikely(!sg))
> + return -ENOMEM;
> +
> + sg_init_table(sg, alloc_size);
> + table->nents = table->orig_nents += sg_size;
> +
> + /*
> + * If this is the first mapping, assign the sg table header.
> + * If this is not the first mapping, chain previous part.
> + */
> + if (prv)
> + sg_chain(prv, SG_MAX_SINGLE_ALLOC, sg);
> + else
> + table->sgl = sg;
> +
> + /*
> + * If no more entries after this one, mark the end
> + */
> + if (!left)
> + sg_mark_end(&sg[sg_size - 1]);
> +
> + /*
> + * only really needed for mempool backed sg allocations (like
> + * SCSI), a possible improvement here would be to pass the
> + * table pointer into the allocator and let that clear these
> + * flags
> + */
> + gfp_mask &= ~__GFP_WAIT;
> + gfp_mask |= __GFP_HIGH;
> + prv = sg;
> + } while (left);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(__sg_alloc_table);
> +
> +/**
> + * sg_alloc_table - Allocate and initialize an sg table
> + * @table: The sg table header to use
> + * @nents: Number of entries in sg list
> + * @gfp_mask: GFP allocation mask
> + *
> + * Description:
> + * Allocate and initialize an sg table. If @nents@ is larger than
> + * SG_MAX_SINGLE_ALLOC a chained sg table will be setup.
> + *
> + **/
> +int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
> +{
> + int ret;
> +
> + ret = __sg_alloc_table(table, nents, gfp_mask, sg_kmalloc);
> + if (unlikely(ret))
> + __sg_free_table(table, sg_kfree);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(sg_alloc_table);
Boaz
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [PATCH 1/3] SG: Move functions to lib/scatterlist.c and add sg chaining allocator helpers
@ 2007-12-20 14:21 ` Boaz Harrosh
0 siblings, 0 replies; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 14:21 UTC (permalink / raw)
To: Jens Axboe, Jens Axboe, James Bottomley, Andrew Morton
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
A small comment in body
On Thu, Dec 20 2007 at 16:00 +0200, Boaz Harrosh <bharrosh@panasas.com> wrote:
> Manually doing chained sg lists is not trivial, so add some helpers
> to make sure that drivers get it right.
>
> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
> ---
> include/linux/scatterlist.h | 125 ++++---------------
> lib/Makefile | 2 +-
> lib/scatterlist.c | 281 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 307 insertions(+), 101 deletions(-)
> create mode 100644 lib/scatterlist.c
>
> diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
> index 416e000..c3ca848 100644
> --- a/include/linux/scatterlist.h
> +++ b/include/linux/scatterlist.h
> @@ -7,6 +7,12 @@
> #include <linux/string.h>
> #include <asm/io.h>
>
> +struct sg_table {
> + struct scatterlist *sgl; /* the list */
> + unsigned int nents; /* number of mapped entries */
> + unsigned int orig_nents; /* original size of list */
> +};
> +
> /*
> * Notes on SG table design.
> *
> @@ -106,31 +112,6 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
> sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
> }
>
> -/**
> - * sg_next - return the next scatterlist entry in a list
> - * @sg: The current sg entry
> - *
> - * Description:
> - * 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.
> - *
> - **/
> -static inline struct scatterlist *sg_next(struct scatterlist *sg)
> -{
> -#ifdef CONFIG_DEBUG_SG
> - BUG_ON(sg->sg_magic != SG_MAGIC);
> -#endif
> - if (sg_is_last(sg))
> - return NULL;
> -
> - 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
> */
> @@ -138,40 +119,6 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg)
> 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
> - *
> - * Description:
> - * 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;
> - unsigned int i;
> -
> - for_each_sg(sgl, sg, nents, i)
> - ret = sg;
> -
> -#endif
> -#ifdef CONFIG_DEBUG_SG
> - BUG_ON(sgl[0].sg_magic != SG_MAGIC);
> - BUG_ON(!sg_is_last(ret));
> -#endif
> - return ret;
> -}
> -
> -/**
> * sg_chain - Chain two sglists together
> * @prv: First scatterlist
> * @prv_nents: Number of entries in prv
> @@ -223,47 +170,6 @@ static inline void sg_mark_end(struct scatterlist *sg)
> }
>
> /**
> - * sg_init_table - Initialize SG table
> - * @sgl: The SG table
> - * @nents: Number of entries in table
> - *
> - * Notes:
> - * If this is part of a chained sg table, sg_mark_end() should be
> - * used only on the last table part.
> - *
> - **/
> -static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
> -{
> - memset(sgl, 0, sizeof(*sgl) * nents);
> -#ifdef CONFIG_DEBUG_SG
> - {
> - unsigned int i;
> - for (i = 0; i < nents; i++)
> - sgl[i].sg_magic = SG_MAGIC;
> - }
> -#endif
> - sg_mark_end(&sgl[nents - 1]);
> -}
> -
> -/**
> - * sg_init_one - Initialize a single entry sg list
> - * @sg: SG entry
> - * @buf: Virtual address for IO
> - * @buflen: IO length
> - *
> - * Notes:
> - * This should not be used on a single entry that is part of a larger
> - * table. Use sg_init_table() for that.
> - *
> - **/
> -static inline void sg_init_one(struct scatterlist *sg, const void *buf,
> - unsigned int buflen)
> -{
> - sg_init_table(sg, 1);
> - sg_set_buf(sg, buf, buflen);
> -}
> -
> -/**
> * sg_phys - Return physical address of an sg entry
> * @sg: SG entry
> *
> @@ -293,4 +199,23 @@ static inline void *sg_virt(struct scatterlist *sg)
> return page_address(sg_page(sg)) + sg->offset;
> }
>
> +struct scatterlist *sg_next(struct scatterlist *);
I don't like that this became exported. I would prefer if
it could stay inline. if at all possible?
> +struct scatterlist *sg_last(struct scatterlist *s, unsigned int);
> +void sg_init_table(struct scatterlist *, unsigned int);
> +void sg_init_one(struct scatterlist *, const void *, unsigned int);
> +
> +typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t);
> +typedef void (sg_free_fn)(struct scatterlist *, unsigned int);
> +
> +void __sg_free_table(struct sg_table *, sg_free_fn *);
> +void sg_free_table(struct sg_table *);
> +int __sg_alloc_table(struct sg_table *, unsigned int, gfp_t, sg_alloc_fn *);
> +int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
> +
> +/*
> + * Maximum number of entries that will be allocated in one piece, if
> + * a list larger than this is required then chaining will be utilized.
> + */
> +#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist))
Yes this is a well needed calculation.
Only now the SCSI part of the allocator is missing.
What is needed is the code a posted a while back that
automatically adjusts pools and sizes to this constant.
I will post that again soon.
(Note that this is a bug on current code since above is 256
in 32 bit code and scsi_lib is only ready for 128)
> +
> #endif /* _LINUX_SCATTERLIST_H */
> diff --git a/lib/Makefile b/lib/Makefile
> index b6793ed..89841dc 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -6,7 +6,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
> rbtree.o radix-tree.o dump_stack.o \
> idr.o int_sqrt.o extable.o prio_tree.o \
> sha1.o irq_regs.o reciprocal_div.o argv_split.o \
> - proportions.o prio_heap.o
> + proportions.o prio_heap.o scatterlist.o
>
> lib-$(CONFIG_MMU) += ioremap.o
> lib-$(CONFIG_SMP) += cpumask.o
> diff --git a/lib/scatterlist.c b/lib/scatterlist.c
> new file mode 100644
> index 0000000..02aaa27
> --- /dev/null
> +++ b/lib/scatterlist.c
> @@ -0,0 +1,281 @@
> +/*
> + * Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com>
> + *
> + * Scatterlist handling helpers.
> + *
> + * This source code is licensed under the GNU General Public License,
> + * Version 2. See the file COPYING for more details.
> + */
> +#include <linux/module.h>
> +#include <linux/scatterlist.h>
> +
> +/**
> + * sg_next - return the next scatterlist entry in a list
> + * @sg: The current sg entry
> + *
> + * Description:
> + * 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.
> + *
> + **/
> +struct scatterlist *sg_next(struct scatterlist *sg)
> +{
> +#ifdef CONFIG_DEBUG_SG
> + BUG_ON(sg->sg_magic != SG_MAGIC);
> +#endif
> + if (sg_is_last(sg))
> + return NULL;
> +
> + sg++;
> + if (unlikely(sg_is_chain(sg)))
> + sg = sg_chain_ptr(sg);
> +
> + return sg;
> +}
> +EXPORT_SYMBOL(sg_next);
> +
> +/**
> + * sg_last - return the last scatterlist entry in a list
> + * @sgl: First entry in the scatterlist
> + * @nents: Number of entries in the scatterlist
> + *
> + * Description:
> + * Should only be used casually, it (currently) scans 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@.
> + *
> + **/
> +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;
> + unsigned int i;
> +
> + for_each_sg(sgl, sg, nents, i)
> + ret = sg;
> +
> +#endif
> +#ifdef CONFIG_DEBUG_SG
> + BUG_ON(sgl[0].sg_magic != SG_MAGIC);
> + BUG_ON(!sg_is_last(ret));
> +#endif
> + return ret;
> +}
> +EXPORT_SYMBOL(sg_last);
> +
> +/**
> + * sg_init_table - Initialize SG table
> + * @sgl: The SG table
> + * @nents: Number of entries in table
> + *
> + * Notes:
> + * If this is part of a chained sg table, sg_mark_end() should be
> + * used only on the last table part.
> + *
> + **/
> +void sg_init_table(struct scatterlist *sgl, unsigned int nents)
> +{
> + memset(sgl, 0, sizeof(*sgl) * nents);
> +#ifdef CONFIG_DEBUG_SG
> + {
> + unsigned int i;
> + for (i = 0; i < nents; i++)
> + sgl[i].sg_magic = SG_MAGIC;
> + }
> +#endif
> + sg_mark_end(&sgl[nents - 1]);
> +}
> +EXPORT_SYMBOL(sg_init_table);
> +
> +/**
> + * sg_init_one - Initialize a single entry sg list
> + * @sg: SG entry
> + * @buf: Virtual address for IO
> + * @buflen: IO length
> + *
> + **/
> +void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen)
> +{
> + sg_init_table(sg, 1);
> + sg_set_buf(sg, buf, buflen);
> +}
> +EXPORT_SYMBOL(sg_init_one);
> +
> +/*
> + * The default behaviour of sg_alloc_table() is to use these kmalloc/kfree
> + * helpers.
> + */
> +static struct scatterlist *sg_kmalloc(unsigned int nents, gfp_t gfp_mask)
> +{
> + if (nents == SG_MAX_SINGLE_ALLOC)
> + return (struct scatterlist *) __get_free_page(gfp_mask);
> + else
> + return kmalloc(nents * sizeof(struct scatterlist), gfp_mask);
> +}
> +
> +static void sg_kfree(struct scatterlist *sg, unsigned int nents)
> +{
> + if (nents == SG_MAX_SINGLE_ALLOC)
> + free_page((unsigned long) sg);
> + else
> + kfree(sg);
> +}
> +
> +/**
> + * __sg_free_table - Free a previously mapped sg table
> + * @table: The sg table header to use
> + * @free_fn: Free function
> + *
> + * Description:
> + * Free an sg table previously allocated and setup with __sg_alloc_table().
> + *
> + **/
> +void __sg_free_table(struct sg_table *table, sg_free_fn *free_fn)
> +{
> + struct scatterlist *sgl, *next;
> +
> + if (unlikely(!table->sgl))
> + return;
> +
> + sgl = table->sgl;
> + while (table->orig_nents) {
> + unsigned int alloc_size = table->orig_nents;
> + unsigned int sg_size;
> +
> + /*
> + * If we have more than SG_MAX_SINGLE_ALLOC segments left,
> + * then assign 'next' to the sg table after the current one.
> + * sg_size is then one less than alloc size, since the last
> + * element is the chain pointer.
> + */
> + if (alloc_size > SG_MAX_SINGLE_ALLOC) {
> + next = sg_chain_ptr(&sgl[SG_MAX_SINGLE_ALLOC - 1]);
> + alloc_size = SG_MAX_SINGLE_ALLOC;
> + sg_size = alloc_size - 1;
> + } else {
> + sg_size = alloc_size;
> + next = NULL;
> + }
> +
> + table->orig_nents -= sg_size;
> + free_fn(sgl, alloc_size);
> + sgl = next;
> + }
> +
> + table->sgl = NULL;
> +}
> +EXPORT_SYMBOL(__sg_free_table);
> +
> +/**
> + * sg_free_table - Free a previously allocated sg table
> + * @table: The mapped sg table header
> + *
> + **/
> +void sg_free_table(struct sg_table *table)
> +{
> + __sg_free_table(table, sg_kfree);
> +}
> +EXPORT_SYMBOL(sg_free_table);
> +
> +/**
> + * __sg_alloc_table - Allocate and initialize an sg table with given allocator
> + * @table: The sg table header to use
> + * @nents: Number of entries in sg list
> + * @gfp_mask: GFP allocation mask
> + * @alloc_fn: Allocator to use
> + *
> + * Notes:
> + * If this function returns non-0 (eg failure), the caller must call
> + * __sg_free_table() to cleanup any leftover allocations.
> + *
> + **/
> +int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask,
> + sg_alloc_fn *alloc_fn)
> +{
> + struct scatterlist *sg, *prv;
> + unsigned int left;
> +
> +#ifndef ARCH_HAS_SG_CHAIN
> + BUG_ON(nents > SG_MAX_SINGLE_ALLOC);
> +#endif
> +
> + memset(table, 0, sizeof(*table));
> +
> + left = nents;
> + prv = NULL;
> + do {
> + unsigned int sg_size, alloc_size = left;
> +
> + if (alloc_size > SG_MAX_SINGLE_ALLOC) {
> + alloc_size = SG_MAX_SINGLE_ALLOC;
> + sg_size = alloc_size - 1;
> + } else
> + sg_size = alloc_size;
> +
> + left -= sg_size;
> +
> + sg = alloc_fn(alloc_size, gfp_mask);
> + if (unlikely(!sg))
> + return -ENOMEM;
> +
> + sg_init_table(sg, alloc_size);
> + table->nents = table->orig_nents += sg_size;
> +
> + /*
> + * If this is the first mapping, assign the sg table header.
> + * If this is not the first mapping, chain previous part.
> + */
> + if (prv)
> + sg_chain(prv, SG_MAX_SINGLE_ALLOC, sg);
> + else
> + table->sgl = sg;
> +
> + /*
> + * If no more entries after this one, mark the end
> + */
> + if (!left)
> + sg_mark_end(&sg[sg_size - 1]);
> +
> + /*
> + * only really needed for mempool backed sg allocations (like
> + * SCSI), a possible improvement here would be to pass the
> + * table pointer into the allocator and let that clear these
> + * flags
> + */
> + gfp_mask &= ~__GFP_WAIT;
> + gfp_mask |= __GFP_HIGH;
> + prv = sg;
> + } while (left);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(__sg_alloc_table);
> +
> +/**
> + * sg_alloc_table - Allocate and initialize an sg table
> + * @table: The sg table header to use
> + * @nents: Number of entries in sg list
> + * @gfp_mask: GFP allocation mask
> + *
> + * Description:
> + * Allocate and initialize an sg table. If @nents@ is larger than
> + * SG_MAX_SINGLE_ALLOC a chained sg table will be setup.
> + *
> + **/
> +int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
> +{
> + int ret;
> +
> + ret = __sg_alloc_table(table, nents, gfp_mask, sg_kmalloc);
> + if (unlikely(ret))
> + __sg_free_table(table, sg_kfree);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(sg_alloc_table);
Boaz
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 1/3] SG: Move functions to lib/scatterlist.c and add sg chaining allocator helpers
2007-12-20 14:00 ` Boaz Harrosh
@ 2007-12-21 12:15 ` Herbert Xu
-1 siblings, 0 replies; 40+ messages in thread
From: Herbert Xu @ 2007-12-21 12:15 UTC (permalink / raw)
To: Boaz Harrosh
Cc: jens.axboe, James.Bottomley, akpm, rusty, fujita.tomonori,
linux-scsi, linux-kernel, dougg
Boaz Harrosh <bharrosh@panasas.com> wrote:
>
> diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
> index 416e000..c3ca848 100644
> --- a/include/linux/scatterlist.h
> +++ b/include/linux/scatterlist.h
> @@ -7,6 +7,12 @@
> #include <linux/string.h>
> #include <asm/io.h>
>
> +struct sg_table {
> + struct scatterlist *sgl; /* the list */
> + unsigned int nents; /* number of mapped entries */
> + unsigned int orig_nents; /* original size of list */
> +};
If we're making massive changes like this, let's do it properly
as Rusty has demonstrated so that we support back-chaining as
well as frong-chaining.
Thanks,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: [PATCH 1/3] SG: Move functions to lib/scatterlist.c and add sg chaining allocator helpers
@ 2007-12-21 12:15 ` Herbert Xu
0 siblings, 0 replies; 40+ messages in thread
From: Herbert Xu @ 2007-12-21 12:15 UTC (permalink / raw)
To: Boaz Harrosh
Cc: jens.axboe, James.Bottomley, akpm, rusty, fujita.tomonori,
linux-scsi, linux-kernel, dougg
Boaz Harrosh <bharrosh@panasas.com> wrote:
>
> diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
> index 416e000..c3ca848 100644
> --- a/include/linux/scatterlist.h
> +++ b/include/linux/scatterlist.h
> @@ -7,6 +7,12 @@
> #include <linux/string.h>
> #include <asm/io.h>
>
> +struct sg_table {
> + struct scatterlist *sgl; /* the list */
> + unsigned int nents; /* number of mapped entries */
> + unsigned int orig_nents; /* original size of list */
> +};
If we're making massive changes like this, let's do it properly
as Rusty has demonstrated so that we support back-chaining as
well as frong-chaining.
Thanks,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] SG: Convert SCSI to use scatterlist helpers for sg chaining
2007-12-20 13:55 ` Boaz Harrosh
(?)
(?)
@ 2007-12-20 14:03 ` Boaz Harrosh
2007-12-20 14:26 ` Boaz Harrosh
-1 siblings, 1 reply; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 14:03 UTC (permalink / raw)
To: Jens Axboe, James Bottomley, Andrew Morton
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
From: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
---
drivers/scsi/libsrp.c | 2 +-
drivers/scsi/scsi_error.c | 4 +-
drivers/scsi/scsi_lib.c | 150 +++++-------------------------------------
drivers/usb/storage/isd200.c | 4 +-
include/scsi/scsi_cmnd.h | 9 +--
5 files changed, 24 insertions(+), 145 deletions(-)
diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
index 8a8562a..b81350d 100644
--- a/drivers/scsi/libsrp.c
+++ b/drivers/scsi/libsrp.c
@@ -427,7 +427,7 @@ int srp_cmd_queue(struct Scsi_Host *shost, struct srp_cmd *cmd, void *info,
sc->SCp.ptr = info;
memcpy(sc->cmnd, cmd->cdb, MAX_COMMAND_SIZE);
sc->sdb.length = len;
- sc->sdb.sglist = (void *) (unsigned long) addr;
+ sc->sdb.sgt.sgl = (void *) (unsigned long) addr;
sc->tag = tag;
err = scsi_tgt_queue_command(sc, itn_id, (struct scsi_lun *)&cmd->lun,
cmd->tag);
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 5c8ba6a..1fd2a8c 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -629,9 +629,9 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
sizeof(scmd->sense_buffer), sense_bytes);
sg_init_one(&ses->sense_sgl, scmd->sense_buffer,
scmd->sdb.length);
- scmd->sdb.sglist = &ses->sense_sgl;
+ scmd->sdb.sgt.sgl = &ses->sense_sgl;
scmd->sc_data_direction = DMA_FROM_DEVICE;
- scmd->sdb.sg_count = 1;
+ scmd->sdb.sgt.nents = 1;
memset(scmd->cmnd, 0, sizeof(scmd->cmnd));
scmd->cmnd[0] = REQUEST_SENSE;
scmd->cmnd[4] = scmd->sdb.length;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index a6aae56..c7107f1 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -750,136 +750,15 @@ static inline unsigned int scsi_sgtable_index(unsigned short nents)
return index;
}
-static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb,
- unsigned short sg_count, gfp_t gfp_mask)
+static struct scatterlist *scsi_sg_alloc(unsigned int nents, gfp_t gfp_mask)
{
- struct scsi_host_sg_pool *sgp;
- struct scatterlist *sgl, *prev, *ret;
- unsigned int index;
- int this, left;
-
- left = sg_count;
- ret = prev = NULL;
- do {
- this = left;
- if (this > SCSI_MAX_SG_SEGMENTS) {
- this = SCSI_MAX_SG_SEGMENTS - 1;
- index = SG_MEMPOOL_NR - 1;
- } else
- index = scsi_sgtable_index(this);
-
- left -= this;
-
- sgp = scsi_sg_pools + index;
-
- sgl = mempool_alloc(sgp->pool, gfp_mask);
- if (unlikely(!sgl))
- goto enomem;
-
- sg_init_table(sgl, sgp->size);
-
- /*
- * first loop through, set initial index and return value
- */
- if (!ret)
- ret = sgl;
-
- /*
- * chain previous sglist, if any. we know the previous
- * sglist must be the biggest one, or we would not have
- * ended up doing another loop.
- */
- if (prev)
- sg_chain(prev, SCSI_MAX_SG_SEGMENTS, sgl);
-
- /*
- * if we have nothing left, mark the last segment as
- * end-of-list
- */
- if (!left)
- sg_mark_end(&sgl[this - 1]);
-
- /*
- * don't allow subsequent mempool allocs to sleep, it would
- * violate the mempool principle.
- */
- gfp_mask &= ~__GFP_WAIT;
- gfp_mask |= __GFP_HIGH;
- prev = sgl;
- } while (left);
-
- /*
- * ->use_sg may get modified after dma mapping has potentially
- * shrunk the number of segments, so keep a copy of it for free.
- */
- sdb->alloc_sg_count = sdb->sg_count = sg_count;
- sdb->sglist = ret;
- return 0;
-enomem:
- if (ret) {
- /*
- * Free entries chained off ret. Since we were trying to
- * allocate another sglist, we know that all entries are of
- * the max size.
- */
- sgp = scsi_sg_pools + SG_MEMPOOL_NR - 1;
- prev = ret;
- ret = &ret[SCSI_MAX_SG_SEGMENTS - 1];
-
- while ((sgl = sg_chain_ptr(ret)) != NULL) {
- ret = &sgl[SCSI_MAX_SG_SEGMENTS - 1];
- mempool_free(sgl, sgp->pool);
- }
-
- mempool_free(prev, sgp->pool);
- }
- return -ENOMEM;
+ return mempool_alloc(scsi_sg_pools[scsi_sgtable_index(nents)].pool,
+ gfp_mask);
}
-static void scsi_free_sgtable(struct scsi_data_buffer *sdb)
+static void scsi_sg_free(struct scatterlist *sgl, unsigned int nents)
{
- struct scatterlist *sgl = sdb->sglist;
- struct scsi_host_sg_pool *sgp;
-
- /*
- * if this is the biggest size sglist, check if we have
- * chained parts we need to free
- */
- if (sdb->alloc_sg_count > SCSI_MAX_SG_SEGMENTS) {
- unsigned short this, left;
- struct scatterlist *next;
- unsigned int index;
-
- left = sdb->alloc_sg_count - (SCSI_MAX_SG_SEGMENTS - 1);
- next = sg_chain_ptr(&sgl[SCSI_MAX_SG_SEGMENTS - 1]);
- while (left && next) {
- sgl = next;
- this = left;
- if (this > SCSI_MAX_SG_SEGMENTS) {
- this = SCSI_MAX_SG_SEGMENTS - 1;
- index = SG_MEMPOOL_NR - 1;
- } else
- index = scsi_sgtable_index(this);
-
- left -= this;
-
- sgp = scsi_sg_pools + index;
-
- if (left)
- next = sg_chain_ptr(&sgl[sgp->size - 1]);
-
- mempool_free(sgl, sgp->pool);
- }
-
- /*
- * Restore original, will be freed below
- */
- sgl = sdb->sglist;
- sgp = scsi_sg_pools + SG_MEMPOOL_NR - 1;
- } else
- sgp = scsi_sg_pools + scsi_sgtable_index(sdb->alloc_sg_count);
-
- mempool_free(sgl, sgp->pool);
+ mempool_free(sgl, scsi_sg_pools[scsi_sgtable_index(nents)].pool);
}
/*
@@ -901,15 +780,15 @@ static void scsi_free_sgtable(struct scsi_data_buffer *sdb)
*/
void scsi_release_buffers(struct scsi_cmnd *cmd)
{
- if (cmd->sdb.sglist)
- scsi_free_sgtable(&cmd->sdb);
+ if (cmd->sdb.sgt.sgl)
+ __sg_free_table(&cmd->sdb.sgt, scsi_sg_free);
memset(&cmd->sdb, 0, sizeof(cmd->sdb));
if (scsi_bidi_cmnd(cmd)) {
struct scsi_data_buffer *bidi_sdb =
cmd->request->next_rq->special;
- scsi_free_sgtable(bidi_sdb);
+ __sg_free_table(&bidi_sdb->sgt, scsi_sg_free);
kmem_cache_free(scsi_bidi_sdb_cache, bidi_sdb);
cmd->request->next_rq->special = NULL;
}
@@ -1135,9 +1014,12 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
gfp_t gfp_mask)
{
- int count;
+ int count, ret;
- if (scsi_alloc_sgtable(sdb, req->nr_phys_segments, gfp_mask)) {
+ ret = __sg_alloc_table(&sdb->sgt, req->nr_phys_segments, gfp_mask,
+ scsi_sg_alloc);
+ if (unlikely(ret)) {
+ __sg_free_table(&sdb->sgt, scsi_sg_free);
return BLKPREP_DEFER;
}
@@ -1151,9 +1033,9 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
* Next, walk the list, and fill in the addresses and sizes of
* each segment.
*/
- count = blk_rq_map_sg(req->q, req, sdb->sglist);
- BUG_ON(count > sdb->sg_count);
- sdb->sg_count = count;
+ count = blk_rq_map_sg(req->q, req, sdb->sgt.sgl);
+ BUG_ON(count > sdb->sgt.nents);
+ sdb->sgt.nents = count;
return BLKPREP_OK;
}
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 2d9a32b..0214ddb 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -415,9 +415,9 @@ static void isd200_set_srb(struct isd200_info *info,
sg_init_one(&info->sg, buff, bufflen);
srb->sc_data_direction = dir;
- srb->sdb.sglist = buff ? &info->sg : NULL;
+ srb->sdb.sgt.sgl = buff ? &info->sg : NULL;
srb->sdb.length = bufflen;
- srb->sdb.sg_count = buff ? 1 : 0;
+ srb->sdb.sgt.nents = buff ? 1 : 0;
}
static void isd200_srb_set_bufflen(struct scsi_cmnd *srb, unsigned bufflen)
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index cd2851a..9933bbe 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -8,16 +8,13 @@
#include <linux/timer.h>
#include <linux/scatterlist.h>
-struct scatterlist;
struct Scsi_Host;
struct scsi_device;
struct scsi_data_buffer {
- struct scatterlist *sglist;
+ struct sg_table sgt;
unsigned length;
int resid;
- unsigned short sg_count;
- unsigned short alloc_sg_count; /* private to scsi_lib */
};
/* embedded in scsi_cmnd */
@@ -136,12 +133,12 @@ extern void scsi_dma_unmap(struct scsi_cmnd *cmd);
static inline unsigned scsi_sg_count(struct scsi_cmnd *cmd)
{
- return cmd->sdb.sg_count;
+ return cmd->sdb.sgt.nents;
}
static inline struct scatterlist *scsi_sglist(struct scsi_cmnd *cmd)
{
- return cmd->sdb.sglist;
+ return cmd->sdb.sgt.sgl;
}
static inline unsigned scsi_bufflen(struct scsi_cmnd *cmd)
--
1.5.3.3
^ permalink raw reply related [flat|nested] 40+ messages in thread* Re: [PATCH 2/3] SG: Convert SCSI to use scatterlist helpers for sg chaining
2007-12-20 14:03 ` [PATCH 2/3] SG: Convert SCSI to use scatterlist helpers for sg chaining Boaz Harrosh
@ 2007-12-20 14:26 ` Boaz Harrosh
0 siblings, 0 replies; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 14:26 UTC (permalink / raw)
To: Jens Axboe, James Bottomley, Andrew Morton
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
On Thu, Dec 20 2007 at 16:03 +0200, Boaz Harrosh <bharrosh@panasas.com> wrote:
> From: Jens Axboe <jens.axboe@oracle.com>
>
> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
> ---
> drivers/scsi/libsrp.c | 2 +-
> drivers/scsi/scsi_error.c | 4 +-
> drivers/scsi/scsi_lib.c | 150 +++++-------------------------------------
> drivers/usb/storage/isd200.c | 4 +-
> include/scsi/scsi_cmnd.h | 9 +--
> 5 files changed, 24 insertions(+), 145 deletions(-)
>
> diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
> index 8a8562a..b81350d 100644
> --- a/drivers/scsi/libsrp.c
> +++ b/drivers/scsi/libsrp.c
> @@ -427,7 +427,7 @@ int srp_cmd_queue(struct Scsi_Host *shost, struct srp_cmd *cmd, void *info,
> sc->SCp.ptr = info;
> memcpy(sc->cmnd, cmd->cdb, MAX_COMMAND_SIZE);
> sc->sdb.length = len;
> - sc->sdb.sglist = (void *) (unsigned long) addr;
> + sc->sdb.sgt.sgl = (void *) (unsigned long) addr;
> sc->tag = tag;
> err = scsi_tgt_queue_command(sc, itn_id, (struct scsi_lun *)&cmd->lun,
> cmd->tag);
> diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
> index 5c8ba6a..1fd2a8c 100644
> --- a/drivers/scsi/scsi_error.c
> +++ b/drivers/scsi/scsi_error.c
> @@ -629,9 +629,9 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
> sizeof(scmd->sense_buffer), sense_bytes);
> sg_init_one(&ses->sense_sgl, scmd->sense_buffer,
> scmd->sdb.length);
> - scmd->sdb.sglist = &ses->sense_sgl;
> + scmd->sdb.sgt.sgl = &ses->sense_sgl;
> scmd->sc_data_direction = DMA_FROM_DEVICE;
> - scmd->sdb.sg_count = 1;
> + scmd->sdb.sgt.nents = 1;
> memset(scmd->cmnd, 0, sizeof(scmd->cmnd));
> scmd->cmnd[0] = REQUEST_SENSE;
> scmd->cmnd[4] = scmd->sdb.length;
> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index a6aae56..c7107f1 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -750,136 +750,15 @@ static inline unsigned int scsi_sgtable_index(unsigned short nents)
> return index;
> }
>
> -static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb,
> - unsigned short sg_count, gfp_t gfp_mask)
> +static struct scatterlist *scsi_sg_alloc(unsigned int nents, gfp_t gfp_mask)
> {
> - struct scsi_host_sg_pool *sgp;
> - struct scatterlist *sgl, *prev, *ret;
> - unsigned int index;
> - int this, left;
> -
> - left = sg_count;
> - ret = prev = NULL;
> - do {
> - this = left;
> - if (this > SCSI_MAX_SG_SEGMENTS) {
> - this = SCSI_MAX_SG_SEGMENTS - 1;
> - index = SG_MEMPOOL_NR - 1;
> - } else
> - index = scsi_sgtable_index(this);
> -
> - left -= this;
> -
> - sgp = scsi_sg_pools + index;
> -
> - sgl = mempool_alloc(sgp->pool, gfp_mask);
> - if (unlikely(!sgl))
> - goto enomem;
> -
> - sg_init_table(sgl, sgp->size);
> -
> - /*
> - * first loop through, set initial index and return value
> - */
> - if (!ret)
> - ret = sgl;
> -
> - /*
> - * chain previous sglist, if any. we know the previous
> - * sglist must be the biggest one, or we would not have
> - * ended up doing another loop.
> - */
> - if (prev)
> - sg_chain(prev, SCSI_MAX_SG_SEGMENTS, sgl);
> -
> - /*
> - * if we have nothing left, mark the last segment as
> - * end-of-list
> - */
> - if (!left)
> - sg_mark_end(&sgl[this - 1]);
> -
> - /*
> - * don't allow subsequent mempool allocs to sleep, it would
> - * violate the mempool principle.
> - */
> - gfp_mask &= ~__GFP_WAIT;
> - gfp_mask |= __GFP_HIGH;
> - prev = sgl;
> - } while (left);
> -
> - /*
> - * ->use_sg may get modified after dma mapping has potentially
> - * shrunk the number of segments, so keep a copy of it for free.
> - */
> - sdb->alloc_sg_count = sdb->sg_count = sg_count;
> - sdb->sglist = ret;
> - return 0;
> -enomem:
> - if (ret) {
> - /*
> - * Free entries chained off ret. Since we were trying to
> - * allocate another sglist, we know that all entries are of
> - * the max size.
> - */
> - sgp = scsi_sg_pools + SG_MEMPOOL_NR - 1;
> - prev = ret;
> - ret = &ret[SCSI_MAX_SG_SEGMENTS - 1];
> -
> - while ((sgl = sg_chain_ptr(ret)) != NULL) {
> - ret = &sgl[SCSI_MAX_SG_SEGMENTS - 1];
> - mempool_free(sgl, sgp->pool);
> - }
> -
> - mempool_free(prev, sgp->pool);
> - }
> - return -ENOMEM;
> + return mempool_alloc(scsi_sg_pools[scsi_sgtable_index(nents)].pool,
> + gfp_mask);
> }
>
> -static void scsi_free_sgtable(struct scsi_data_buffer *sdb)
> +static void scsi_sg_free(struct scatterlist *sgl, unsigned int nents)
> {
> - struct scatterlist *sgl = sdb->sglist;
> - struct scsi_host_sg_pool *sgp;
> -
> - /*
> - * if this is the biggest size sglist, check if we have
> - * chained parts we need to free
> - */
> - if (sdb->alloc_sg_count > SCSI_MAX_SG_SEGMENTS) {
> - unsigned short this, left;
> - struct scatterlist *next;
> - unsigned int index;
> -
> - left = sdb->alloc_sg_count - (SCSI_MAX_SG_SEGMENTS - 1);
> - next = sg_chain_ptr(&sgl[SCSI_MAX_SG_SEGMENTS - 1]);
> - while (left && next) {
> - sgl = next;
> - this = left;
> - if (this > SCSI_MAX_SG_SEGMENTS) {
> - this = SCSI_MAX_SG_SEGMENTS - 1;
> - index = SG_MEMPOOL_NR - 1;
> - } else
> - index = scsi_sgtable_index(this);
> -
> - left -= this;
> -
> - sgp = scsi_sg_pools + index;
> -
> - if (left)
> - next = sg_chain_ptr(&sgl[sgp->size - 1]);
> -
> - mempool_free(sgl, sgp->pool);
> - }
> -
> - /*
> - * Restore original, will be freed below
> - */
> - sgl = sdb->sglist;
> - sgp = scsi_sg_pools + SG_MEMPOOL_NR - 1;
> - } else
> - sgp = scsi_sg_pools + scsi_sgtable_index(sdb->alloc_sg_count);
> -
> - mempool_free(sgl, sgp->pool);
> + mempool_free(sgl, scsi_sg_pools[scsi_sgtable_index(nents)].pool);
> }
>
> /*
> @@ -901,15 +780,15 @@ static void scsi_free_sgtable(struct scsi_data_buffer *sdb)
> */
> void scsi_release_buffers(struct scsi_cmnd *cmd)
> {
> - if (cmd->sdb.sglist)
> - scsi_free_sgtable(&cmd->sdb);
> + if (cmd->sdb.sgt.sgl)
> + __sg_free_table(&cmd->sdb.sgt, scsi_sg_free);
>
> memset(&cmd->sdb, 0, sizeof(cmd->sdb));
>
> if (scsi_bidi_cmnd(cmd)) {
> struct scsi_data_buffer *bidi_sdb =
> cmd->request->next_rq->special;
> - scsi_free_sgtable(bidi_sdb);
> + __sg_free_table(&bidi_sdb->sgt, scsi_sg_free);
> kmem_cache_free(scsi_bidi_sdb_cache, bidi_sdb);
> cmd->request->next_rq->special = NULL;
> }
> @@ -1135,9 +1014,12 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
> static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
> gfp_t gfp_mask)
> {
> - int count;
> + int count, ret;
>
> - if (scsi_alloc_sgtable(sdb, req->nr_phys_segments, gfp_mask)) {
> + ret = __sg_alloc_table(&sdb->sgt, req->nr_phys_segments, gfp_mask,
> + scsi_sg_alloc);
> + if (unlikely(ret)) {
> + __sg_free_table(&sdb->sgt, scsi_sg_free);
> return BLKPREP_DEFER;
> }
>
> @@ -1151,9 +1033,9 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
> * Next, walk the list, and fill in the addresses and sizes of
> * each segment.
> */
> - count = blk_rq_map_sg(req->q, req, sdb->sglist);
> - BUG_ON(count > sdb->sg_count);
> - sdb->sg_count = count;
> + count = blk_rq_map_sg(req->q, req, sdb->sgt.sgl);
> + BUG_ON(count > sdb->sgt.nents);
> + sdb->sgt.nents = count;
> return BLKPREP_OK;
> }
>
> diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
> index 2d9a32b..0214ddb 100644
> --- a/drivers/usb/storage/isd200.c
> +++ b/drivers/usb/storage/isd200.c
> @@ -415,9 +415,9 @@ static void isd200_set_srb(struct isd200_info *info,
> sg_init_one(&info->sg, buff, bufflen);
>
> srb->sc_data_direction = dir;
> - srb->sdb.sglist = buff ? &info->sg : NULL;
> + srb->sdb.sgt.sgl = buff ? &info->sg : NULL;
> srb->sdb.length = bufflen;
> - srb->sdb.sg_count = buff ? 1 : 0;
> + srb->sdb.sgt.nents = buff ? 1 : 0;
> }
>
> static void isd200_srb_set_bufflen(struct scsi_cmnd *srb, unsigned bufflen)
> diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
> index cd2851a..9933bbe 100644
> --- a/include/scsi/scsi_cmnd.h
> +++ b/include/scsi/scsi_cmnd.h
> @@ -8,16 +8,13 @@
> #include <linux/timer.h>
> #include <linux/scatterlist.h>
>
> -struct scatterlist;
> struct Scsi_Host;
> struct scsi_device;
>
> struct scsi_data_buffer {
> - struct scatterlist *sglist;
> + struct sg_table sgt;
> unsigned length;
> int resid;
> - unsigned short sg_count;
> - unsigned short alloc_sg_count; /* private to scsi_lib */
> };
>
> /* embedded in scsi_cmnd */
> @@ -136,12 +133,12 @@ extern void scsi_dma_unmap(struct scsi_cmnd *cmd);
>
> static inline unsigned scsi_sg_count(struct scsi_cmnd *cmd)
> {
> - return cmd->sdb.sg_count;
> + return cmd->sdb.sgt.nents;
> }
>
> static inline struct scatterlist *scsi_sglist(struct scsi_cmnd *cmd)
> {
> - return cmd->sdb.sglist;
> + return cmd->sdb.sgt.sgl;
> }
>
> static inline unsigned scsi_bufflen(struct scsi_cmnd *cmd)
The cmd->sdb.sgt.sgl thingy is a bit ugly, I would say. There is
not much we can do, unless Jens is willing to add the "length" and
"resid" members into his struct sg_table. I guess other drivers might
have use for them other than scsi. Jens could you do this for us?
Boaz
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] SG: Update ide/ to use sg_table
2007-12-20 13:55 ` Boaz Harrosh
` (2 preceding siblings ...)
(?)
@ 2007-12-20 14:06 ` Boaz Harrosh
-1 siblings, 0 replies; 40+ messages in thread
From: Boaz Harrosh @ 2007-12-20 14:06 UTC (permalink / raw)
To: Jens Axboe, James Bottomley, Andrew Morton
Cc: Rusty Russell, FUJITA Tomonori, linux-scsi, linux-kernel, dougg
From: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
---
drivers/ide/arm/icside.c | 6 +++---
drivers/ide/cris/ide-cris.c | 2 +-
drivers/ide/ide-dma.c | 8 ++++----
drivers/ide/ide-io.c | 2 +-
drivers/ide/ide-probe.c | 6 +-----
drivers/ide/ide-taskfile.c | 2 +-
drivers/ide/ide.c | 2 +-
drivers/ide/mips/au1xxx-ide.c | 8 ++++----
drivers/ide/pci/sgiioc4.c | 4 ++--
drivers/ide/ppc/pmac.c | 6 +++---
drivers/scsi/ide-scsi.c | 2 +-
include/linux/ide.h | 2 +-
12 files changed, 23 insertions(+), 27 deletions(-)
diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c
index 93f71fc..a48a6bd 100644
--- a/drivers/ide/arm/icside.c
+++ b/drivers/ide/arm/icside.c
@@ -210,7 +210,7 @@ static void icside_build_sglist(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = drive->hwif;
struct icside_state *state = hwif->hwif_data;
- struct scatterlist *sg = hwif->sg_table;
+ struct scatterlist *sg = hwif->sg_table.sgl;
ide_map_sg(drive, rq);
@@ -319,7 +319,7 @@ static int icside_dma_end(ide_drive_t *drive)
disable_dma(ECARD_DEV(state->dev)->dma);
/* Teardown mappings after DMA has completed. */
- dma_unmap_sg(state->dev, hwif->sg_table, hwif->sg_nents,
+ dma_unmap_sg(state->dev, hwif->sg_table.sgl, hwif->sg_nents,
hwif->sg_dma_direction);
return get_dma_residue(ECARD_DEV(state->dev)->dma) != 0;
@@ -373,7 +373,7 @@ static int icside_dma_setup(ide_drive_t *drive)
* Tell the DMA engine about the SG table and
* data direction.
*/
- set_dma_sg(ECARD_DEV(state->dev)->dma, hwif->sg_table, hwif->sg_nents);
+ set_dma_sg(ECARD_DEV(state->dev)->dma, hwif->sg_table.sgl, hwif->sg_nents);
set_dma_mode(ECARD_DEV(state->dev)->dma, dma_mode);
drive->waiting_for_dma = 1;
diff --git a/drivers/ide/cris/ide-cris.c b/drivers/ide/cris/ide-cris.c
index 476e0d6..3701aca 100644
--- a/drivers/ide/cris/ide-cris.c
+++ b/drivers/ide/cris/ide-cris.c
@@ -919,7 +919,7 @@ static int cris_ide_build_dmatable (ide_drive_t *drive)
unsigned int count = 0;
int i = 0;
- sg = hwif->sg_table;
+ sg = hwif->sg_table.sgl;
ata_tot_size = 0;
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index 4703837..33a2f56 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -190,7 +190,7 @@ static int ide_dma_good_drive(ide_drive_t *drive)
int ide_build_sglist(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
- struct scatterlist *sg = hwif->sg_table;
+ struct scatterlist *sg = hwif->sg_table.sgl;
BUG_ON((rq->cmd_type == REQ_TYPE_ATA_TASKFILE) && rq->nr_sectors > 256);
@@ -234,7 +234,7 @@ int ide_build_dmatable (ide_drive_t *drive, struct request *rq)
if (!i)
return 0;
- sg = hwif->sg_table;
+ sg = hwif->sg_table.sgl;
while (i) {
u32 cur_addr;
u32 cur_len;
@@ -293,7 +293,7 @@ int ide_build_dmatable (ide_drive_t *drive, struct request *rq)
printk(KERN_ERR "%s: empty DMA table?\n", drive->name);
use_pio_instead:
pci_unmap_sg(hwif->pci_dev,
- hwif->sg_table,
+ hwif->sg_table.sgl,
hwif->sg_nents,
hwif->sg_dma_direction);
return 0; /* revert to PIO for this request */
@@ -315,7 +315,7 @@ EXPORT_SYMBOL_GPL(ide_build_dmatable);
void ide_destroy_dmatable (ide_drive_t *drive)
{
struct pci_dev *dev = HWIF(drive)->pci_dev;
- struct scatterlist *sg = HWIF(drive)->sg_table;
+ struct scatterlist *sg = HWIF(drive)->sg_table.sgl;
int nents = HWIF(drive)->sg_nents;
pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction);
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index bef781f..638e2db 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -819,7 +819,7 @@ static ide_startstop_t do_special (ide_drive_t *drive)
void ide_map_sg(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = drive->hwif;
- struct scatterlist *sg = hwif->sg_table;
+ struct scatterlist *sg = hwif->sg_table.sgl;
if (hwif->sg_mapped) /* needed by ide-scsi */
return;
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 2994523..770f8cf 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -1312,15 +1312,11 @@ 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,
- GFP_KERNEL);
- if (!hwif->sg_table) {
+ if (sg_alloc_table(&hwif->sg_table, hwif->sg_max_nents, GFP_KERNEL)) {
printk(KERN_ERR "%s: unable to allocate SG table.\n", hwif->name);
goto out;
}
- sg_init_table(hwif->sg_table, hwif->sg_max_nents);
-
if (init_irq(hwif) == 0)
goto done;
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 2b60f1b..0a898d9 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -246,7 +246,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive)
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 *sg = hwif->sg_table.sgl;
struct scatterlist *cursg = hwif->cursg;
struct page *page;
#ifdef CONFIG_HIGHMEM
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 54943da..a2ad85a 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -594,7 +594,7 @@ void ide_unregister(unsigned int index)
* Remove us from the kernel's knowledge
*/
blk_unregister_region(MKDEV(hwif->major, 0), MAX_DRIVES<<PARTN_BITS);
- kfree(hwif->sg_table);
+ sg_free_table(&hwif->sg_table);
unregister_blkdev(hwif->major, hwif->name);
spin_lock_irq(&ide_lock);
diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c
index a4ce3ba..bccf3c0 100644
--- a/drivers/ide/mips/au1xxx-ide.c
+++ b/drivers/ide/mips/au1xxx-ide.c
@@ -216,7 +216,7 @@ static int auide_build_sglist(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = drive->hwif;
_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
- struct scatterlist *sg = hwif->sg_table;
+ struct scatterlist *sg = hwif->sg_table.sgl;
ide_map_sg(drive, rq);
@@ -250,7 +250,7 @@ static int auide_build_dmatable(ide_drive_t *drive)
return 0;
/* fill the descriptors */
- sg = hwif->sg_table;
+ sg = hwif->sg_table.sgl;
while (i && sg_dma_len(sg)) {
u32 cur_addr;
u32 cur_len;
@@ -303,7 +303,7 @@ static int auide_build_dmatable(ide_drive_t *drive)
use_pio_instead:
dma_unmap_sg(ahwif->dev,
- hwif->sg_table,
+ hwif->sg_table.sgl,
hwif->sg_nents,
hwif->sg_dma_direction);
@@ -316,7 +316,7 @@ static int auide_dma_end(ide_drive_t *drive)
_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
if (hwif->sg_nents) {
- dma_unmap_sg(ahwif->dev, hwif->sg_table, hwif->sg_nents,
+ dma_unmap_sg(ahwif->dev, hwif->sg_table.sgl, hwif->sg_nents,
hwif->sg_dma_direction);
hwif->sg_nents = 0;
}
diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c
index de820aa..3ad9e4b 100644
--- a/drivers/ide/pci/sgiioc4.c
+++ b/drivers/ide/pci/sgiioc4.c
@@ -487,7 +487,7 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
if (!i)
return 0; /* sglist of length Zero */
- sg = hwif->sg_table;
+ sg = hwif->sg_table.sgl;
while (i && sg_dma_len(sg)) {
dma_addr_t cur_addr;
int cur_len;
@@ -535,7 +535,7 @@ sgiioc4_build_dma_table(ide_drive_t * drive, struct request *rq, int ddir)
}
use_pio_instead:
- pci_unmap_sg(hwif->pci_dev, hwif->sg_table, hwif->sg_nents,
+ pci_unmap_sg(hwif->pci_dev, hwif->sg_table.sgl, hwif->sg_nents,
hwif->sg_dma_direction);
return 0; /* revert to PIO for this request */
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index 7f7a598..b982180 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -1503,7 +1503,7 @@ pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq)
return 0;
/* Build DBDMA commands list */
- sg = hwif->sg_table;
+ sg = hwif->sg_table.sgl;
while (i && sg_dma_len(sg)) {
u32 cur_addr;
u32 cur_len;
@@ -1555,7 +1555,7 @@ pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq)
printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name);
use_pio_instead:
pci_unmap_sg(hwif->pci_dev,
- hwif->sg_table,
+ hwif->sg_table.sgl,
hwif->sg_nents,
hwif->sg_dma_direction);
return 0; /* revert to PIO for this request */
@@ -1567,7 +1567,7 @@ pmac_ide_destroy_dmatable (ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = HWIF(drive)->pci_dev;
- struct scatterlist *sg = hwif->sg_table;
+ struct scatterlist *sg = hwif->sg_table.sgl;
int nents = hwif->sg_nents;
if (nents) {
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 9706de9..762bd93 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -553,7 +553,7 @@ static int idescsi_map_sg(ide_drive_t *drive, idescsi_pc_t *pc)
if (idescsi_set_direction(pc))
return 1;
- sg = hwif->sg_table;
+ sg = hwif->sg_table.sgl;
scsi_sg = scsi_sglist(pc->scsi_cmd);
segments = scsi_sg_count(pc->scsi_cmd);
diff --git a/include/linux/ide.h b/include/linux/ide.h
index 9a6a41e..80ff2ee 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -750,7 +750,7 @@ typedef struct hwif_s {
/* dma physical region descriptor table (dma view) */
dma_addr_t dmatable_dma;
/* Scatter-gather list used to build the above */
- struct scatterlist *sg_table;
+ struct sg_table sg_table;
int sg_max_nents; /* Maximum number of entries in it */
int sg_nents; /* Current number of entries in it */
int sg_dma_direction; /* dma transfer direction */
--
1.5.3.3
^ permalink raw reply related [flat|nested] 40+ messages in thread