Commit 7cedb1f1 authored by James Bottomley's avatar James Bottomley Committed by Jens Axboe

SG: work with the SCSI fixed maximum allocations.

SCSI sg table allocation has a maximum size (of SCSI_MAX_SG_SEGMENTS,
currently 128) and this will cause a BUG_ON() in SCSI if something
tries an allocation over it.  This patch adds a size limit to the
chaining allocator to allow the specification of the maximum
allocation size for chaining, so we always chain in units of the
maximum SCSI allocation size.
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: default avatarJens Axboe <jens.axboe@oracle.com>
parent 5ed7959e
...@@ -761,9 +761,11 @@ int scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) ...@@ -761,9 +761,11 @@ int scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask)
BUG_ON(!cmd->use_sg); BUG_ON(!cmd->use_sg);
ret = __sg_alloc_table(&cmd->sg_table, cmd->use_sg, gfp_mask, scsi_sg_alloc); ret = __sg_alloc_table(&cmd->sg_table, cmd->use_sg,
SCSI_MAX_SG_SEGMENTS, gfp_mask, scsi_sg_alloc);
if (unlikely(ret)) if (unlikely(ret))
__sg_free_table(&cmd->sg_table, scsi_sg_free); __sg_free_table(&cmd->sg_table, SCSI_MAX_SG_SEGMENTS,
scsi_sg_free);
cmd->request_buffer = cmd->sg_table.sgl; cmd->request_buffer = cmd->sg_table.sgl;
return ret; return ret;
...@@ -773,7 +775,7 @@ EXPORT_SYMBOL(scsi_alloc_sgtable); ...@@ -773,7 +775,7 @@ EXPORT_SYMBOL(scsi_alloc_sgtable);
void scsi_free_sgtable(struct scsi_cmnd *cmd) void scsi_free_sgtable(struct scsi_cmnd *cmd)
{ {
__sg_free_table(&cmd->sg_table, scsi_sg_free); __sg_free_table(&cmd->sg_table, SCSI_MAX_SG_SEGMENTS, scsi_sg_free);
} }
EXPORT_SYMBOL(scsi_free_sgtable); EXPORT_SYMBOL(scsi_free_sgtable);
......
...@@ -207,9 +207,10 @@ void sg_init_one(struct scatterlist *, const void *, unsigned int); ...@@ -207,9 +207,10 @@ void sg_init_one(struct scatterlist *, const void *, unsigned int);
typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t); typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t);
typedef void (sg_free_fn)(struct scatterlist *, unsigned int); 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 *, unsigned int, sg_free_fn *);
void sg_free_table(struct sg_table *); 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, unsigned int, gfp_t,
sg_alloc_fn *);
int sg_alloc_table(struct sg_table *, unsigned int, gfp_t); int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
/* /*
......
...@@ -130,13 +130,17 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents) ...@@ -130,13 +130,17 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents)
/** /**
* __sg_free_table - Free a previously mapped sg table * __sg_free_table - Free a previously mapped sg table
* @table: The sg table header to use * @table: The sg table header to use
* @max_ents: The maximum number of entries per single scatterlist
* @free_fn: Free function * @free_fn: Free function
* *
* Description: * Description:
* Free an sg table previously allocated and setup with __sg_alloc_table(). * Free an sg table previously allocated and setup with
* __sg_alloc_table(). The @max_ents value must be identical to
* that previously used with __sg_alloc_table().
* *
**/ **/
void __sg_free_table(struct sg_table *table, sg_free_fn *free_fn) void __sg_free_table(struct sg_table *table, unsigned int max_ents,
sg_free_fn *free_fn)
{ {
struct scatterlist *sgl, *next; struct scatterlist *sgl, *next;
...@@ -149,14 +153,14 @@ void __sg_free_table(struct sg_table *table, sg_free_fn *free_fn) ...@@ -149,14 +153,14 @@ void __sg_free_table(struct sg_table *table, sg_free_fn *free_fn)
unsigned int sg_size; unsigned int sg_size;
/* /*
* If we have more than SG_MAX_SINGLE_ALLOC segments left, * If we have more than max_ents segments left,
* then assign 'next' to the sg table after the current one. * then assign 'next' to the sg table after the current one.
* sg_size is then one less than alloc size, since the last * sg_size is then one less than alloc size, since the last
* element is the chain pointer. * element is the chain pointer.
*/ */
if (alloc_size > SG_MAX_SINGLE_ALLOC) { if (alloc_size > max_ents) {
next = sg_chain_ptr(&sgl[SG_MAX_SINGLE_ALLOC - 1]); next = sg_chain_ptr(&sgl[max_ents - 1]);
alloc_size = SG_MAX_SINGLE_ALLOC; alloc_size = max_ents;
sg_size = alloc_size - 1; sg_size = alloc_size - 1;
} else { } else {
sg_size = alloc_size; sg_size = alloc_size;
...@@ -179,7 +183,7 @@ EXPORT_SYMBOL(__sg_free_table); ...@@ -179,7 +183,7 @@ EXPORT_SYMBOL(__sg_free_table);
**/ **/
void sg_free_table(struct sg_table *table) void sg_free_table(struct sg_table *table)
{ {
__sg_free_table(table, sg_kfree); __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
} }
EXPORT_SYMBOL(sg_free_table); EXPORT_SYMBOL(sg_free_table);
...@@ -187,22 +191,30 @@ EXPORT_SYMBOL(sg_free_table); ...@@ -187,22 +191,30 @@ EXPORT_SYMBOL(sg_free_table);
* __sg_alloc_table - Allocate and initialize an sg table with given allocator * __sg_alloc_table - Allocate and initialize an sg table with given allocator
* @table: The sg table header to use * @table: The sg table header to use
* @nents: Number of entries in sg list * @nents: Number of entries in sg list
* @max_ents: The maximum number of entries the allocator returns per call
* @gfp_mask: GFP allocation mask * @gfp_mask: GFP allocation mask
* @alloc_fn: Allocator to use * @alloc_fn: Allocator to use
* *
* Description:
* This function returns a @table @nents long. The allocator is
* defined to return scatterlist chunks of maximum size @max_ents.
* Thus if @nents is bigger than @max_ents, the scatterlists will be
* chained in units of @max_ents.
*
* Notes: * Notes:
* If this function returns non-0 (eg failure), the caller must call * If this function returns non-0 (eg failure), the caller must call
* __sg_free_table() to cleanup any leftover allocations. * __sg_free_table() to cleanup any leftover allocations.
* *
**/ **/
int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask, int __sg_alloc_table(struct sg_table *table, unsigned int nents,
unsigned int max_ents, gfp_t gfp_mask,
sg_alloc_fn *alloc_fn) sg_alloc_fn *alloc_fn)
{ {
struct scatterlist *sg, *prv; struct scatterlist *sg, *prv;
unsigned int left; unsigned int left;
#ifndef ARCH_HAS_SG_CHAIN #ifndef ARCH_HAS_SG_CHAIN
BUG_ON(nents > SG_MAX_SINGLE_ALLOC); BUG_ON(nents > max_ents);
#endif #endif
memset(table, 0, sizeof(*table)); memset(table, 0, sizeof(*table));
...@@ -212,8 +224,8 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask, ...@@ -212,8 +224,8 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask,
do { do {
unsigned int sg_size, alloc_size = left; unsigned int sg_size, alloc_size = left;
if (alloc_size > SG_MAX_SINGLE_ALLOC) { if (alloc_size > max_ents) {
alloc_size = SG_MAX_SINGLE_ALLOC; alloc_size = max_ents;
sg_size = alloc_size - 1; sg_size = alloc_size - 1;
} else } else
sg_size = alloc_size; sg_size = alloc_size;
...@@ -232,7 +244,7 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask, ...@@ -232,7 +244,7 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask,
* If this is not the first mapping, chain previous part. * If this is not the first mapping, chain previous part.
*/ */
if (prv) if (prv)
sg_chain(prv, SG_MAX_SINGLE_ALLOC, sg); sg_chain(prv, max_ents, sg);
else else
table->sgl = sg; table->sgl = sg;
...@@ -272,9 +284,10 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) ...@@ -272,9 +284,10 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
{ {
int ret; int ret;
ret = __sg_alloc_table(table, nents, gfp_mask, sg_kmalloc); ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC,
gfp_mask, sg_kmalloc);
if (unlikely(ret)) if (unlikely(ret))
__sg_free_table(table, sg_kfree); __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment