diff options
author | Martin K. Petersen <martin.petersen@oracle.com> | 2008-07-18 01:08:48 +0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-26 23:14:55 +0400 |
commit | 7027ad72a689797475973c6feb5f0b673382f779 (patch) | |
tree | 4f6daa1d509708fb340d09608d79557a9af57f00 /drivers/scsi | |
parent | db007fc5e20c00b356e9ffe2d0e007398c65c837 (diff) | |
download | linux-7027ad72a689797475973c6feb5f0b673382f779.tar.xz |
[SCSI] Support devices with protection information
Implement support for DMA of protection information for devices that
are data integrity capable.
- Add support for mapping an extra scatter-gather list containing
the protection information.
- Allocate protection scsi_data_buffer if host is DIX (integrity DMA)
capable.
- Accessor function for checking whether a device has protection
enabled.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/scsi.c | 36 | ||||
-rw-r--r-- | drivers/scsi/scsi_lib.c | 25 | ||||
-rw-r--r-- | drivers/scsi/scsi_priv.h | 1 |
3 files changed, 59 insertions, 3 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 5276e73c58fc..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; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index fe77ccacf319..a47c05d7829a 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -65,7 +65,7 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = { }; #undef SP -static struct kmem_cache *scsi_sdb_cache; +struct kmem_cache *scsi_sdb_cache; static void scsi_run_queue(struct request_queue *q); @@ -787,6 +787,9 @@ void scsi_release_buffers(struct scsi_cmnd *cmd) kmem_cache_free(scsi_sdb_cache, bidi_sdb); cmd->request->next_rq->special = NULL; } + + if (scsi_prot_sg_count(cmd)) + scsi_free_sgtable(cmd->prot_sdb); } EXPORT_SYMBOL(scsi_release_buffers); @@ -1072,6 +1075,26 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) goto err_exit; } + if (blk_integrity_rq(cmd->request)) { + struct scsi_data_buffer *prot_sdb = cmd->prot_sdb; + int ivecs, count; + + BUG_ON(prot_sdb == NULL); + ivecs = blk_rq_count_integrity_sg(cmd->request); + + if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask)) { + error = BLKPREP_DEFER; + goto err_exit; + } + + count = blk_rq_map_integrity_sg(cmd->request, + prot_sdb->table.sgl); + BUG_ON(unlikely(count > ivecs)); + + cmd->prot_sdb = prot_sdb; + cmd->prot_sdb->table.nents = count; + } + return BLKPREP_OK ; err_exit: diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index b33e72516ef8..79f0f7511204 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -77,6 +77,7 @@ extern void scsi_exit_queue(void); struct request_queue; struct request; extern int scsi_prep_fn(struct request_queue *, struct request *); +extern struct kmem_cache *scsi_sdb_cache; /* scsi_proc.c */ #ifdef CONFIG_SCSI_PROC_FS |