diff options
author | Christoph Hellwig <hch@infradead.org> | 2014-02-20 17:21:01 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2014-03-27 11:26:33 -0400 |
commit | 89d9a567952baec13e26ada3e438f1b642d66b6e (patch) | |
tree | f6217370ee634dc3fa08228a0aa1073265aa13b7 /drivers | |
parent | 7c283341225d0ebeb7480a9e6560f599dcd0f417 (diff) |
[SCSI] add support for per-host cmd pools
This allows drivers to specify the size of their per-command private
data in the host template and then get extra memory allocated for
each command instead of needing another allocation in ->queuecommand.
With the current SCSI code that already does multiple allocations for
each command this probably doesn't make a big performance impact, but
it allows to clean up the drivers, and prepare them for using the
blk-mq infrastructure where the common allocation will make a difference.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/scsi.c | 96 |
1 files changed, 77 insertions, 19 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 586c241c941e..c4d632c27a3e 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c | |||
@@ -331,46 +331,103 @@ void scsi_put_command(struct scsi_cmnd *cmd) | |||
331 | } | 331 | } |
332 | EXPORT_SYMBOL(scsi_put_command); | 332 | EXPORT_SYMBOL(scsi_put_command); |
333 | 333 | ||
334 | static struct scsi_host_cmd_pool *scsi_get_host_cmd_pool(gfp_t gfp_mask) | 334 | static struct scsi_host_cmd_pool * |
335 | scsi_find_host_cmd_pool(struct Scsi_Host *shost) | ||
335 | { | 336 | { |
337 | if (shost->hostt->cmd_size) | ||
338 | return shost->hostt->cmd_pool; | ||
339 | if (shost->unchecked_isa_dma) | ||
340 | return &scsi_cmd_dma_pool; | ||
341 | return &scsi_cmd_pool; | ||
342 | } | ||
343 | |||
344 | static void | ||
345 | scsi_free_host_cmd_pool(struct scsi_host_cmd_pool *pool) | ||
346 | { | ||
347 | kfree(pool->sense_name); | ||
348 | kfree(pool->cmd_name); | ||
349 | kfree(pool); | ||
350 | } | ||
351 | |||
352 | static struct scsi_host_cmd_pool * | ||
353 | scsi_alloc_host_cmd_pool(struct Scsi_Host *shost) | ||
354 | { | ||
355 | struct scsi_host_template *hostt = shost->hostt; | ||
356 | struct scsi_host_cmd_pool *pool; | ||
357 | |||
358 | pool = kzalloc(sizeof(*pool), GFP_KERNEL); | ||
359 | if (!pool) | ||
360 | return NULL; | ||
361 | |||
362 | pool->cmd_name = kasprintf(GFP_KERNEL, "%s_cmd", hostt->name); | ||
363 | pool->sense_name = kasprintf(GFP_KERNEL, "%s_sense", hostt->name); | ||
364 | if (!pool->cmd_name || !pool->sense_name) { | ||
365 | scsi_free_host_cmd_pool(pool); | ||
366 | return NULL; | ||
367 | } | ||
368 | |||
369 | pool->slab_flags = SLAB_HWCACHE_ALIGN; | ||
370 | if (shost->unchecked_isa_dma) { | ||
371 | pool->slab_flags |= SLAB_CACHE_DMA; | ||
372 | pool->gfp_mask = __GFP_DMA; | ||
373 | } | ||
374 | return pool; | ||
375 | } | ||
376 | |||
377 | static struct scsi_host_cmd_pool * | ||
378 | scsi_get_host_cmd_pool(struct Scsi_Host *shost) | ||
379 | { | ||
380 | struct scsi_host_template *hostt = shost->hostt; | ||
336 | struct scsi_host_cmd_pool *retval = NULL, *pool; | 381 | struct scsi_host_cmd_pool *retval = NULL, *pool; |
382 | size_t cmd_size = sizeof(struct scsi_cmnd) + hostt->cmd_size; | ||
383 | |||
337 | /* | 384 | /* |
338 | * Select a command slab for this host and create it if not | 385 | * Select a command slab for this host and create it if not |
339 | * yet existent. | 386 | * yet existent. |
340 | */ | 387 | */ |
341 | mutex_lock(&host_cmd_pool_mutex); | 388 | mutex_lock(&host_cmd_pool_mutex); |
342 | pool = (gfp_mask & __GFP_DMA) ? &scsi_cmd_dma_pool : | 389 | pool = scsi_find_host_cmd_pool(shost); |
343 | &scsi_cmd_pool; | 390 | if (!pool) { |
391 | pool = scsi_alloc_host_cmd_pool(shost); | ||
392 | if (!pool) | ||
393 | goto out; | ||
394 | } | ||
395 | |||
344 | if (!pool->users) { | 396 | if (!pool->users) { |
345 | pool->cmd_slab = kmem_cache_create(pool->cmd_name, | 397 | pool->cmd_slab = kmem_cache_create(pool->cmd_name, cmd_size, 0, |
346 | sizeof(struct scsi_cmnd), 0, | ||
347 | pool->slab_flags, NULL); | 398 | pool->slab_flags, NULL); |
348 | if (!pool->cmd_slab) | 399 | if (!pool->cmd_slab) |
349 | goto fail; | 400 | goto out_free_pool; |
350 | 401 | ||
351 | pool->sense_slab = kmem_cache_create(pool->sense_name, | 402 | pool->sense_slab = kmem_cache_create(pool->sense_name, |
352 | SCSI_SENSE_BUFFERSIZE, 0, | 403 | SCSI_SENSE_BUFFERSIZE, 0, |
353 | pool->slab_flags, NULL); | 404 | pool->slab_flags, NULL); |
354 | if (!pool->sense_slab) { | 405 | if (!pool->sense_slab) |
355 | kmem_cache_destroy(pool->cmd_slab); | 406 | goto out_free_slab; |
356 | goto fail; | ||
357 | } | ||
358 | } | 407 | } |
359 | 408 | ||
360 | pool->users++; | 409 | pool->users++; |
361 | retval = pool; | 410 | retval = pool; |
362 | fail: | 411 | out: |
363 | mutex_unlock(&host_cmd_pool_mutex); | 412 | mutex_unlock(&host_cmd_pool_mutex); |
364 | return retval; | 413 | return retval; |
414 | |||
415 | out_free_slab: | ||
416 | kmem_cache_destroy(pool->cmd_slab); | ||
417 | out_free_pool: | ||
418 | if (hostt->cmd_size) | ||
419 | scsi_free_host_cmd_pool(pool); | ||
420 | goto out; | ||
365 | } | 421 | } |
366 | 422 | ||
367 | static void scsi_put_host_cmd_pool(gfp_t gfp_mask) | 423 | static void scsi_put_host_cmd_pool(struct Scsi_Host *shost) |
368 | { | 424 | { |
425 | struct scsi_host_template *hostt = shost->hostt; | ||
369 | struct scsi_host_cmd_pool *pool; | 426 | struct scsi_host_cmd_pool *pool; |
370 | 427 | ||
371 | mutex_lock(&host_cmd_pool_mutex); | 428 | mutex_lock(&host_cmd_pool_mutex); |
372 | pool = (gfp_mask & __GFP_DMA) ? &scsi_cmd_dma_pool : | 429 | pool = scsi_find_host_cmd_pool(shost); |
373 | &scsi_cmd_pool; | 430 | |
374 | /* | 431 | /* |
375 | * This may happen if a driver has a mismatched get and put | 432 | * This may happen if a driver has a mismatched get and put |
376 | * of the command pool; the driver should be implicated in | 433 | * of the command pool; the driver should be implicated in |
@@ -381,6 +438,8 @@ static void scsi_put_host_cmd_pool(gfp_t gfp_mask) | |||
381 | if (!--pool->users) { | 438 | if (!--pool->users) { |
382 | kmem_cache_destroy(pool->cmd_slab); | 439 | kmem_cache_destroy(pool->cmd_slab); |
383 | kmem_cache_destroy(pool->sense_slab); | 440 | kmem_cache_destroy(pool->sense_slab); |
441 | if (hostt->cmd_size) | ||
442 | scsi_free_host_cmd_pool(pool); | ||
384 | } | 443 | } |
385 | mutex_unlock(&host_cmd_pool_mutex); | 444 | mutex_unlock(&host_cmd_pool_mutex); |
386 | } | 445 | } |
@@ -397,14 +456,13 @@ static void scsi_put_host_cmd_pool(gfp_t gfp_mask) | |||
397 | */ | 456 | */ |
398 | int scsi_setup_command_freelist(struct Scsi_Host *shost) | 457 | int scsi_setup_command_freelist(struct Scsi_Host *shost) |
399 | { | 458 | { |
400 | struct scsi_cmnd *cmd; | ||
401 | const gfp_t gfp_mask = shost->unchecked_isa_dma ? GFP_DMA : GFP_KERNEL; | 459 | const gfp_t gfp_mask = shost->unchecked_isa_dma ? GFP_DMA : GFP_KERNEL; |
460 | struct scsi_cmnd *cmd; | ||
402 | 461 | ||
403 | spin_lock_init(&shost->free_list_lock); | 462 | spin_lock_init(&shost->free_list_lock); |
404 | INIT_LIST_HEAD(&shost->free_list); | 463 | INIT_LIST_HEAD(&shost->free_list); |
405 | 464 | ||
406 | shost->cmd_pool = scsi_get_host_cmd_pool(gfp_mask); | 465 | shost->cmd_pool = scsi_get_host_cmd_pool(shost); |
407 | |||
408 | if (!shost->cmd_pool) | 466 | if (!shost->cmd_pool) |
409 | return -ENOMEM; | 467 | return -ENOMEM; |
410 | 468 | ||
@@ -413,7 +471,7 @@ int scsi_setup_command_freelist(struct Scsi_Host *shost) | |||
413 | */ | 471 | */ |
414 | cmd = scsi_host_alloc_command(shost, gfp_mask); | 472 | cmd = scsi_host_alloc_command(shost, gfp_mask); |
415 | if (!cmd) { | 473 | if (!cmd) { |
416 | scsi_put_host_cmd_pool(gfp_mask); | 474 | scsi_put_host_cmd_pool(shost); |
417 | shost->cmd_pool = NULL; | 475 | shost->cmd_pool = NULL; |
418 | return -ENOMEM; | 476 | return -ENOMEM; |
419 | } | 477 | } |
@@ -442,7 +500,7 @@ void scsi_destroy_command_freelist(struct Scsi_Host *shost) | |||
442 | scsi_host_free_command(shost, cmd); | 500 | scsi_host_free_command(shost, cmd); |
443 | } | 501 | } |
444 | shost->cmd_pool = NULL; | 502 | shost->cmd_pool = NULL; |
445 | scsi_put_host_cmd_pool(shost->unchecked_isa_dma ? GFP_DMA : GFP_KERNEL); | 503 | scsi_put_host_cmd_pool(shost); |
446 | } | 504 | } |
447 | 505 | ||
448 | #ifdef CONFIG_SCSI_LOGGING | 506 | #ifdef CONFIG_SCSI_LOGGING |