]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: James Smart <james.smart@emulex.com> |
2 | Subject: scsi_lib_dma.c : fix bug w/ dma on virtual fc ports | |
3 | References: bnc#431294 | |
4 | ||
5 | When the updated scsi dma code was introduced recently, it assumed | |
6 | the physical host/adapter was the parent of the scsi host. | |
7 | Unfortunately, on FC virtual ports, the parent of the scsi host is | |
8 | the virtual port, which does not have dma information. | |
9 | ||
10 | I have updated the dma routines to use a function that finds the | |
11 | first non-scsi object. A non-scsi object is defined to be an object | |
12 | that has a non-NULL type (assumes all transport objects have NULL | |
13 | types) or a non-scsi_host type. | |
14 | ||
15 | -- james s | |
16 | ||
17 | Unfortunately the original patch is not correct, as eg the PCI device | |
18 | doesn't set the 'type' pointer, so the system will crash miserably. | |
19 | We should rather check for the 'bus' argument, and return the original | |
20 | argument if we don't find anything. | |
21 | ||
22 | Signed-off-by: Hannes Reinecke <hare@suse.de> | |
23 | ||
24 | diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c | |
25 | index 69321e3..208e7df 100644 | |
26 | --- a/drivers/scsi/scsi_lib.c | |
27 | +++ b/drivers/scsi/scsi_lib.c | |
28 | @@ -1713,7 +1713,7 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, | |
29 | request_fn_proc *request_fn) | |
30 | { | |
31 | struct request_queue *q; | |
32 | - struct device *dev = shost->shost_gendev.parent; | |
33 | + struct device *dev = dev_to_nonscsi_dev(shost->shost_gendev.parent); | |
34 | ||
35 | q = blk_init_queue(request_fn, NULL); | |
36 | if (!q) | |
37 | diff --git a/drivers/scsi/scsi_lib_dma.c b/drivers/scsi/scsi_lib_dma.c | |
38 | index ac6855c..567cdbc 100644 | |
39 | --- a/drivers/scsi/scsi_lib_dma.c | |
40 | +++ b/drivers/scsi/scsi_lib_dma.c | |
41 | @@ -23,7 +23,8 @@ int scsi_dma_map(struct scsi_cmnd *cmd) | |
42 | int nseg = 0; | |
43 | ||
44 | if (scsi_sg_count(cmd)) { | |
45 | - struct device *dev = cmd->device->host->shost_gendev.parent; | |
46 | + struct device *dev = dev_to_nonscsi_dev( | |
47 | + cmd->device->host->shost_gendev.parent); | |
48 | ||
49 | nseg = dma_map_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), | |
50 | cmd->sc_data_direction); | |
51 | @@ -41,10 +42,12 @@ EXPORT_SYMBOL(scsi_dma_map); | |
52 | void scsi_dma_unmap(struct scsi_cmnd *cmd) | |
53 | { | |
54 | if (scsi_sg_count(cmd)) { | |
55 | - struct device *dev = cmd->device->host->shost_gendev.parent; | |
56 | + struct device *dev = dev_to_nonscsi_dev( | |
57 | + cmd->device->host->shost_gendev.parent); | |
58 | ||
59 | dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd), | |
60 | cmd->sc_data_direction); | |
61 | } | |
62 | } | |
63 | EXPORT_SYMBOL(scsi_dma_unmap); | |
64 | + | |
65 | diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h | |
66 | index d123ca8..55d74c8 100644 | |
67 | --- a/include/scsi/scsi_host.h | |
68 | +++ b/include/scsi/scsi_host.h | |
69 | @@ -689,6 +689,10 @@ static inline void *shost_priv(struct Scsi_Host *shost) | |
70 | ||
71 | int scsi_is_host_device(const struct device *); | |
72 | ||
73 | +/* | |
74 | + * walks object list backward, to find the first shost object. | |
75 | + * Skips over transport objects that may not be stargets, etc | |
76 | + */ | |
77 | static inline struct Scsi_Host *dev_to_shost(struct device *dev) | |
78 | { | |
79 | while (!scsi_is_host_device(dev)) { | |
80 | @@ -699,6 +703,26 @@ static inline struct Scsi_Host *dev_to_shost(struct device *dev) | |
81 | return container_of(dev, struct Scsi_Host, shost_gendev); | |
82 | } | |
83 | ||
84 | +/* | |
85 | + * walks object list backward, to find the first physical | |
86 | + * device object. If none is found return the original device. | |
87 | + */ | |
88 | +static inline struct device *dev_to_nonscsi_dev(struct device *dev) | |
89 | +{ | |
90 | + struct device *orig = dev; | |
91 | + | |
92 | + while (dev && (dev->bus == NULL || scsi_is_host_device(dev))) { | |
93 | + if (dev->dma_parms) { | |
94 | + dev_printk(KERN_WARNING, dev, | |
95 | + "dma_parms set, bus %p\n", | |
96 | + dev->bus); | |
97 | + break; | |
98 | + } | |
99 | + dev = dev->parent; | |
100 | + } | |
101 | + return dev?dev:orig; | |
102 | +} | |
103 | + | |
104 | static inline int scsi_host_in_recovery(struct Scsi_Host *shost) | |
105 | { | |
106 | return shost->shost_state == SHOST_RECOVERY || |