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: James Bottomley <James.Bottomley@HansenPartnership.com> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
This commit is contained in:
committed by
Jens Axboe
parent
5ed7959ede
commit
7cedb1f17f
@ -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);
|
|||||||
|
|
||||||
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);
|
|||||||
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)
|
|||||||
/**
|
/**
|
||||||
* __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)
|
|||||||
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);
|
|||||||
**/
|
**/
|
||||||
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);
|
|||||||
* __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,
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user