diff options
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r-- | drivers/scsi/scsi.c | 64 |
1 files changed, 57 insertions, 7 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 110e776d1a07..ee6be596503d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -197,11 +197,43 @@ static void scsi_pool_free_command(struct scsi_host_cmd_pool *pool, struct scsi_cmnd *cmd) { + if (cmd->prot_sdb) + kmem_cache_free(scsi_sdb_cache, cmd->prot_sdb); + kmem_cache_free(pool->sense_slab, cmd->sense_buffer); kmem_cache_free(pool->cmd_slab, cmd); } /** + * scsi_host_alloc_command - internal function to allocate command + * @shost: SCSI host whose pool to allocate from + * @gfp_mask: mask for the allocation + * + * Returns a fully allocated command with sense buffer and protection + * data buffer (where applicable) or NULL on failure + */ +static struct scsi_cmnd * +scsi_host_alloc_command(struct Scsi_Host *shost, gfp_t gfp_mask) +{ + struct scsi_cmnd *cmd; + + cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); + if (!cmd) + return NULL; + + if (scsi_host_get_prot(shost) >= SHOST_DIX_TYPE0_PROTECTION) { + cmd->prot_sdb = kmem_cache_zalloc(scsi_sdb_cache, gfp_mask); + + if (!cmd->prot_sdb) { + scsi_pool_free_command(shost->cmd_pool, cmd); + return NULL; + } + } + + return cmd; +} + +/** * __scsi_get_command - Allocate a struct scsi_cmnd * @shost: host to transmit command * @gfp_mask: allocation mask @@ -214,7 +246,7 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) struct scsi_cmnd *cmd; unsigned char *buf; - cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); + cmd = scsi_host_alloc_command(shost, gfp_mask); if (unlikely(!cmd)) { unsigned long flags; @@ -457,7 +489,7 @@ int scsi_setup_command_freelist(struct Scsi_Host *shost) /* * Get one backup command for this host. */ - cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); + cmd = scsi_host_alloc_command(shost, gfp_mask); if (!cmd) { scsi_put_host_cmd_pool(gfp_mask); shost->cmd_pool = NULL; @@ -855,9 +887,18 @@ void scsi_finish_command(struct scsi_cmnd *cmd) good_bytes = scsi_bufflen(cmd); if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) { + int old_good_bytes = good_bytes; drv = scsi_cmd_to_driver(cmd); if (drv->done) good_bytes = drv->done(cmd); + /* + * USB may not give sense identifying bad sector and + * simply return a residue instead, so subtract off the + * residue if drv->done() error processing indicates no + * change to the completion length. + */ + if (good_bytes == old_good_bytes) + good_bytes -= scsi_get_resid(cmd); } scsi_io_completion(cmd, good_bytes); } @@ -893,11 +934,20 @@ void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - /* Check to see if the queue is managed by the block layer. - * If it is, and we fail to adjust the depth, exit. */ - if (blk_queue_tagged(sdev->request_queue) && - blk_queue_resize_tags(sdev->request_queue, tags) != 0) - goto out; + /* + * Check to see if the queue is managed by the block layer. + * If it is, and we fail to adjust the depth, exit. + * + * Do not resize the tag map if it is a host wide share bqt, + * because the size should be the hosts's can_queue. If there + * is more IO than the LLD's can_queue (so there are not enuogh + * tags) request_fn's host queue ready check will handle it. + */ + if (!sdev->host->bqt) { + if (blk_queue_tagged(sdev->request_queue) && + blk_queue_resize_tags(sdev->request_queue, tags) != 0) + goto out; + } sdev->queue_depth = tags; switch (tagged) { |