summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin K. Petersen <martin.petersen@oracle.com>2008-07-18 01:08:48 +0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-07-26 23:14:55 +0400
commit7027ad72a689797475973c6feb5f0b673382f779 (patch)
tree4f6daa1d509708fb340d09608d79557a9af57f00
parentdb007fc5e20c00b356e9ffe2d0e007398c65c837 (diff)
downloadlinux-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>
-rw-r--r--drivers/scsi/scsi.c36
-rw-r--r--drivers/scsi/scsi_lib.c25
-rw-r--r--drivers/scsi/scsi_priv.h1
-rw-r--r--include/scsi/scsi_cmnd.h20
-rw-r--r--include/scsi/scsi_device.h5
5 files changed, 84 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
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index 402c1078d01c..f9f6e793575c 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -90,6 +90,8 @@ struct scsi_cmnd {
/* These elements define the operation we ultimately want to perform */
struct scsi_data_buffer sdb;
+ struct scsi_data_buffer *prot_sdb;
+
unsigned underflow; /* Return error if less than
this amount is transferred */
@@ -274,4 +276,22 @@ static inline sector_t scsi_get_lba(struct scsi_cmnd *scmd)
return scmd->request->sector;
}
+static inline unsigned scsi_prot_sg_count(struct scsi_cmnd *cmd)
+{
+ return cmd->prot_sdb ? cmd->prot_sdb->table.nents : 0;
+}
+
+static inline struct scatterlist *scsi_prot_sglist(struct scsi_cmnd *cmd)
+{
+ return cmd->prot_sdb ? cmd->prot_sdb->table.sgl : NULL;
+}
+
+static inline struct scsi_data_buffer *scsi_prot(struct scsi_cmnd *cmd)
+{
+ return cmd->prot_sdb;
+}
+
+#define scsi_for_each_prot_sg(cmd, sg, nseg, __i) \
+ for_each_sg(scsi_prot_sglist(cmd), sg, nseg, __i)
+
#endif /* _SCSI_SCSI_CMND_H */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 4deb9349eebf..9cecc409f0f8 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -423,6 +423,11 @@ static inline int scsi_device_enclosure(struct scsi_device *sdev)
return sdev->inquiry[6] & (1<<6);
}
+static inline int scsi_device_protection(struct scsi_device *sdev)
+{
+ return sdev->inquiry[5] & (1<<0);
+}
+
#define MODULE_ALIAS_SCSI_DEVICE(type) \
MODULE_ALIAS("scsi:t-" __stringify(type) "*")
#define SCSI_DEVICE_MODALIAS_FMT "scsi:t-0x%02x"