Skip to content

Commit c53c6d6

Browse files
author
Christoph Hellwig
committed
scatterlist: allow chaining to preallocated chunks
Blk-mq drivers usually preallocate their S/G list as part of the request, but if we want to support the very large S/G lists currently supported by the SCSI code that would tie up a lot of memory in the preallocated request pool. Add support to the scatterlist code so that it can initialize a S/G list that uses a preallocated first chunks and dynamically allocated additional chunks. That way the scsi-mq code can preallocate a first page worth of S/G entries as part of the request, and dynamically extend the S/G list when needed. Signed-off-by: Christoph Hellwig <[email protected]> Reviewed-by: Martin K. Petersen <[email protected]> Reviewed-by: Hannes Reinecke <[email protected]> Reviewed-by: Webb Scales <[email protected]> Acked-by: Jens Axboe <[email protected]> Tested-by: Bart Van Assche <[email protected]> Tested-by: Robert Elliott <[email protected]>
1 parent f6d47e7 commit c53c6d6

File tree

3 files changed

+27
-20
lines changed

3 files changed

+27
-20
lines changed

drivers/scsi/scsi_lib.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,11 @@ static struct scatterlist *scsi_sg_alloc(unsigned int nents, gfp_t gfp_mask)
564564
return mempool_alloc(sgp->pool, gfp_mask);
565565
}
566566

567+
static void scsi_free_sgtable(struct scsi_data_buffer *sdb)
568+
{
569+
__sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, false, scsi_sg_free);
570+
}
571+
567572
static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents,
568573
gfp_t gfp_mask)
569574
{
@@ -572,19 +577,12 @@ static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents,
572577
BUG_ON(!nents);
573578

574579
ret = __sg_alloc_table(&sdb->table, nents, SCSI_MAX_SG_SEGMENTS,
575-
gfp_mask, scsi_sg_alloc);
580+
NULL, gfp_mask, scsi_sg_alloc);
576581
if (unlikely(ret))
577-
__sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS,
578-
scsi_sg_free);
579-
582+
scsi_free_sgtable(sdb);
580583
return ret;
581584
}
582585

583-
static void scsi_free_sgtable(struct scsi_data_buffer *sdb)
584-
{
585-
__sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, scsi_sg_free);
586-
}
587-
588586
/*
589587
* Function: scsi_release_buffers()
590588
*

include/linux/scatterlist.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@ void sg_init_one(struct scatterlist *, const void *, unsigned int);
229229
typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t);
230230
typedef void (sg_free_fn)(struct scatterlist *, unsigned int);
231231

232-
void __sg_free_table(struct sg_table *, unsigned int, sg_free_fn *);
232+
void __sg_free_table(struct sg_table *, unsigned int, bool, sg_free_fn *);
233233
void sg_free_table(struct sg_table *);
234-
int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, gfp_t,
235-
sg_alloc_fn *);
234+
int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int,
235+
struct scatterlist *, gfp_t, sg_alloc_fn *);
236236
int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
237237
int sg_alloc_table_from_pages(struct sg_table *sgt,
238238
struct page **pages, unsigned int n_pages,

lib/scatterlist.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents)
165165
* __sg_free_table - Free a previously mapped sg table
166166
* @table: The sg table header to use
167167
* @max_ents: The maximum number of entries per single scatterlist
168+
* @skip_first_chunk: don't free the (preallocated) first scatterlist chunk
168169
* @free_fn: Free function
169170
*
170171
* Description:
@@ -174,7 +175,7 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents)
174175
*
175176
**/
176177
void __sg_free_table(struct sg_table *table, unsigned int max_ents,
177-
sg_free_fn *free_fn)
178+
bool skip_first_chunk, sg_free_fn *free_fn)
178179
{
179180
struct scatterlist *sgl, *next;
180181

@@ -202,7 +203,10 @@ void __sg_free_table(struct sg_table *table, unsigned int max_ents,
202203
}
203204

204205
table->orig_nents -= sg_size;
205-
free_fn(sgl, alloc_size);
206+
if (!skip_first_chunk) {
207+
free_fn(sgl, alloc_size);
208+
skip_first_chunk = false;
209+
}
206210
sgl = next;
207211
}
208212

@@ -217,7 +221,7 @@ EXPORT_SYMBOL(__sg_free_table);
217221
**/
218222
void sg_free_table(struct sg_table *table)
219223
{
220-
__sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
224+
__sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree);
221225
}
222226
EXPORT_SYMBOL(sg_free_table);
223227

@@ -241,8 +245,8 @@ EXPORT_SYMBOL(sg_free_table);
241245
*
242246
**/
243247
int __sg_alloc_table(struct sg_table *table, unsigned int nents,
244-
unsigned int max_ents, gfp_t gfp_mask,
245-
sg_alloc_fn *alloc_fn)
248+
unsigned int max_ents, struct scatterlist *first_chunk,
249+
gfp_t gfp_mask, sg_alloc_fn *alloc_fn)
246250
{
247251
struct scatterlist *sg, *prv;
248252
unsigned int left;
@@ -269,7 +273,12 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
269273

270274
left -= sg_size;
271275

272-
sg = alloc_fn(alloc_size, gfp_mask);
276+
if (first_chunk) {
277+
sg = first_chunk;
278+
first_chunk = NULL;
279+
} else {
280+
sg = alloc_fn(alloc_size, gfp_mask);
281+
}
273282
if (unlikely(!sg)) {
274283
/*
275284
* Adjust entry count to reflect that the last
@@ -324,9 +333,9 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
324333
int ret;
325334

326335
ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC,
327-
gfp_mask, sg_kmalloc);
336+
NULL, gfp_mask, sg_kmalloc);
328337
if (unlikely(ret))
329-
__sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
338+
__sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree);
330339

331340
return ret;
332341
}

0 commit comments

Comments
 (0)