From: James Smart Subject: scsi_lib_dma.c : fix bug w/ dma on virtual fc ports References: bnc#431294 When the updated scsi dma code was introduced recently, it assumed the physical host/adapter was the parent of the scsi host. Unfortunately, on FC virtual ports, the parent of the scsi host is the virtual port, which does not have dma information. I have updated the dma routines to use a function that finds the first non-scsi object. A non-scsi object is defined to be an object that has a non-NULL type (assumes all transport objects have NULL types) or a non-scsi_host type. -- james s Unfortunately the original patch is not correct, as eg the PCI device doesn't set the 'type' pointer, so the system will crash miserably. We should rather check for the 'bus' argument, and return the original argument if we don't find anything. Signed-off-by: Hannes Reinecke diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 69321e3..208e7df 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1713,7 +1713,7 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, request_fn_proc *request_fn) { struct request_queue *q; - struct device *dev = shost->shost_gendev.parent; + struct device *dev = dev_to_nonscsi_dev(shost->shost_gendev.parent); q = blk_init_queue(request_fn, NULL); if (!q) diff --git a/drivers/scsi/scsi_lib_dma.c b/drivers/scsi/scsi_lib_dma.c index ac6855c..567cdbc 100644 --- a/drivers/scsi/scsi_lib_dma.c +++ b/drivers/scsi/scsi_lib_dma.c @@ -23,7 +23,8 @@ int scsi_dma_map(struct scsi_cmnd *cmd) int nseg = 0; if (scsi_sg_count(cmd)) { - struct device *dev = cmd->device->host->shost_gendev.parent; + struct device *dev = dev_to_nonscsi_dev( + cmd->device->host->shost_gendev.parent); nseg = dma_map_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), cmd->sc_data_direction); @@ -41,10 +42,12 @@ EXPORT_SYMBOL(scsi_dma_map); void scsi_dma_unmap(struct scsi_cmnd *cmd) { if (scsi_sg_count(cmd)) { - struct device *dev = cmd->device->host->shost_gendev.parent; + struct device *dev = dev_to_nonscsi_dev( + cmd->device->host->shost_gendev.parent); dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), cmd->sc_data_direction); } } EXPORT_SYMBOL(scsi_dma_unmap); + diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index d123ca8..55d74c8 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -689,6 +689,10 @@ static inline void *shost_priv(struct Scsi_Host *shost) int scsi_is_host_device(const struct device *); +/* + * walks object list backward, to find the first shost object. + * Skips over transport objects that may not be stargets, etc + */ static inline struct Scsi_Host *dev_to_shost(struct device *dev) { while (!scsi_is_host_device(dev)) { @@ -699,6 +703,26 @@ static inline struct Scsi_Host *dev_to_shost(struct device *dev) return container_of(dev, struct Scsi_Host, shost_gendev); } +/* + * walks object list backward, to find the first physical + * device object. If none is found return the original device. + */ +static inline struct device *dev_to_nonscsi_dev(struct device *dev) +{ + struct device *orig = dev; + + while (dev && (dev->bus == NULL || scsi_is_host_device(dev))) { + if (dev->dma_parms) { + dev_printk(KERN_WARNING, dev, + "dma_parms set, bus %p\n", + dev->bus); + break; + } + dev = dev->parent; + } + return dev?dev:orig; +} + static inline int scsi_host_in_recovery(struct Scsi_Host *shost) { return shost->shost_state == SHOST_RECOVERY ||