]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Hannes Reinecke <hare@suse.de> |
2 | Subject: cciss: Ignore stale commands after reboot | |
3 | References: bnc#513437 | |
4 | Patch-Mainline: yes | |
5 | ||
6 | When doing an unexpected shutdown like kexec the cciss | |
7 | firmware might still have some commands in flight, which | |
8 | it is trying to complete. | |
9 | The driver is doing it's best on resetting the HBA, | |
10 | but sadly there's a firmware issue causing the firmware | |
11 | _not_ to abort or drop old commands. | |
12 | So the firmware will send us commands which we haven't | |
13 | accounted for, causing the driver to panic. | |
14 | ||
15 | This patch also updates the queue handling to using | |
16 | kernel-provided hashed lists; without it we wouldn't | |
17 | be able to detect stale commands at all. | |
18 | Queue handling is backported from mainline. | |
19 | ||
20 | Signed-off-by: Hannes Reinecke <hare@suse.de> | |
21 | ||
22 | diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c | |
23 | index d7ec8e4..28d3f3d 100644 | |
24 | --- a/drivers/block/cciss.c | |
25 | +++ b/drivers/block/cciss.c | |
26 | @@ -214,31 +214,27 @@ static struct block_device_operations cciss_fops = { | |
27 | /* | |
28 | * Enqueuing and dequeuing functions for cmdlists. | |
29 | */ | |
30 | -static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c) | |
31 | +static inline void addQ(struct hlist_head *list, CommandList_struct *c) | |
32 | { | |
33 | - if (*Qptr == NULL) { | |
34 | - *Qptr = c; | |
35 | - c->next = c->prev = c; | |
36 | - } else { | |
37 | - c->prev = (*Qptr)->prev; | |
38 | - c->next = (*Qptr); | |
39 | - (*Qptr)->prev->next = c; | |
40 | - (*Qptr)->prev = c; | |
41 | - } | |
42 | + hlist_add_head(&c->list, list); | |
43 | } | |
44 | ||
45 | -static inline CommandList_struct *removeQ(CommandList_struct **Qptr, | |
46 | - CommandList_struct *c) | |
47 | +static inline void removeQ(CommandList_struct *c) | |
48 | { | |
49 | - if (c && c->next != c) { | |
50 | - if (*Qptr == c) | |
51 | - *Qptr = c->next; | |
52 | - c->prev->next = c->next; | |
53 | - c->next->prev = c->prev; | |
54 | - } else { | |
55 | - *Qptr = NULL; | |
56 | + /* | |
57 | + * After kexec/dump some commands might still | |
58 | + * be in flight, which the firmware will try | |
59 | + * to complete. Resetting the firmware doesn't work | |
60 | + * with old fw revisions, so we have to mark | |
61 | + * them off as 'stale' to prevent the driver from | |
62 | + * falling over. | |
63 | + */ | |
64 | + if (unlikely(hlist_unhashed(&c->list))) { | |
65 | + c->cmd_type = CMD_MSG_STALE; | |
66 | + return; | |
67 | } | |
68 | - return c; | |
69 | + | |
70 | + hlist_del_init(&c->list); | |
71 | } | |
72 | ||
73 | #include "cciss_scsi.c" /* For SCSI tape support */ | |
74 | @@ -505,6 +501,7 @@ static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool) | |
75 | c->cmdindex = i; | |
76 | } | |
77 | ||
78 | + INIT_HLIST_NODE(&c->list); | |
79 | c->busaddr = (__u32) cmd_dma_handle; | |
80 | temp64.val = (__u64) err_dma_handle; | |
81 | c->ErrDesc.Addr.lower = temp64.val32.lower; | |
82 | @@ -2549,7 +2548,8 @@ static void start_io(ctlr_info_t *h) | |
83 | { | |
84 | CommandList_struct *c; | |
85 | ||
86 | - while ((c = h->reqQ) != NULL) { | |
87 | + while (!hlist_empty(&h->reqQ)) { | |
88 | + c = hlist_entry(h->reqQ.first, CommandList_struct, list); | |
89 | /* can't do anything if fifo is full */ | |
90 | if ((h->access.fifo_full(h))) { | |
91 | printk(KERN_WARNING "cciss: fifo full\n"); | |
92 | @@ -2557,14 +2557,14 @@ static void start_io(ctlr_info_t *h) | |
93 | } | |
94 | ||
95 | /* Get the first entry from the Request Q */ | |
96 | - removeQ(&(h->reqQ), c); | |
97 | + removeQ(c); | |
98 | h->Qdepth--; | |
99 | ||
100 | /* Tell the controller execute command */ | |
101 | h->access.submit_command(h, c); | |
102 | ||
103 | /* Put job onto the completed Q */ | |
104 | - addQ(&(h->cmpQ), c); | |
105 | + addQ(&h->cmpQ, c); | |
106 | } | |
107 | } | |
108 | ||
109 | @@ -2577,7 +2577,7 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c) | |
110 | memset(c->err_info, 0, sizeof(ErrorInfo_struct)); | |
111 | ||
112 | /* add it to software queue and then send it to the controller */ | |
113 | - addQ(&(h->reqQ), c); | |
114 | + addQ(&h->reqQ, c); | |
115 | h->Qdepth++; | |
116 | if (h->Qdepth > h->maxQsinceinit) | |
117 | h->maxQsinceinit = h->Qdepth; | |
118 | @@ -2898,7 +2898,7 @@ static void do_cciss_request(struct request_queue *q) | |
119 | ||
120 | spin_lock_irq(q->queue_lock); | |
121 | ||
122 | - addQ(&(h->reqQ), c); | |
123 | + addQ(&h->reqQ, c); | |
124 | h->Qdepth++; | |
125 | if (h->Qdepth > h->maxQsinceinit) | |
126 | h->maxQsinceinit = h->Qdepth; | |
127 | @@ -2986,16 +2986,12 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id) | |
128 | a = c->busaddr; | |
129 | ||
130 | } else { | |
131 | + struct hlist_node *tmp; | |
132 | + | |
133 | a &= ~3; | |
134 | - if ((c = h->cmpQ) == NULL) { | |
135 | - printk(KERN_WARNING | |
136 | - "cciss: Completion of %08x ignored\n", | |
137 | - a1); | |
138 | - continue; | |
139 | - } | |
140 | - while (c->busaddr != a) { | |
141 | - c = c->next; | |
142 | - if (c == h->cmpQ) | |
143 | + c = NULL; | |
144 | + hlist_for_each_entry(c, tmp, &h->cmpQ, list) { | |
145 | + if (c->busaddr == a) | |
146 | break; | |
147 | } | |
148 | } | |
149 | @@ -3003,8 +2999,8 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id) | |
150 | * If we've found the command, take it off the | |
151 | * completion Q and free it | |
152 | */ | |
153 | - if (c->busaddr == a) { | |
154 | - removeQ(&h->cmpQ, c); | |
155 | + if (c && c->busaddr == a) { | |
156 | + removeQ(c); | |
157 | if (c->cmd_type == CMD_RWREQ) { | |
158 | complete_command(h, c, 0); | |
159 | } else if (c->cmd_type == CMD_IOCTL_PEND) { | |
160 | @@ -3423,6 +3419,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, | |
161 | return -1; | |
162 | ||
163 | hba[i]->busy_initializing = 1; | |
164 | + INIT_HLIST_HEAD(&hba[i]->cmpQ); | |
165 | + INIT_HLIST_HEAD(&hba[i]->reqQ); | |
166 | ||
167 | if (cciss_pci_init(hba[i], pdev) != 0) | |
168 | goto clean1; | |
169 | @@ -3730,16 +3728,19 @@ static void fail_all_cmds(unsigned long ctlr) | |
170 | pci_disable_device(h->pdev); /* Make sure it is really dead. */ | |
171 | ||
172 | /* move everything off the request queue onto the completed queue */ | |
173 | - while ((c = h->reqQ) != NULL) { | |
174 | - removeQ(&(h->reqQ), c); | |
175 | + while (!hlist_empty(&h->reqQ)) { | |
176 | + c = hlist_entry(h->reqQ.first, CommandList_struct, list); | |
177 | + removeQ(c); | |
178 | h->Qdepth--; | |
179 | - addQ(&(h->cmpQ), c); | |
180 | + addQ(&h->cmpQ, c); | |
181 | } | |
182 | ||
183 | /* Now, fail everything on the completed queue with a HW error */ | |
184 | - while ((c = h->cmpQ) != NULL) { | |
185 | - removeQ(&h->cmpQ, c); | |
186 | - c->err_info->CommandStatus = CMD_HARDWARE_ERR; | |
187 | + while (!hlist_empty(&h->cmpQ)) { | |
188 | + c = hlist_entry(h->cmpQ.first, CommandList_struct, list); | |
189 | + removeQ(c); | |
190 | + if (c->cmd_type != CMD_MSG_STALE) | |
191 | + c->err_info->CommandStatus = CMD_HARDWARE_ERR; | |
192 | if (c->cmd_type == CMD_RWREQ) { | |
193 | complete_command(h, c, 0); | |
194 | } else if (c->cmd_type == CMD_IOCTL_PEND) | |
195 | diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h | |
196 | index 24a7efa..15e2b84 100644 | |
197 | --- a/drivers/block/cciss.h | |
198 | +++ b/drivers/block/cciss.h | |
199 | @@ -89,8 +89,8 @@ struct ctlr_info | |
200 | struct access_method access; | |
201 | ||
202 | /* queue and queue Info */ | |
203 | - CommandList_struct *reqQ; | |
204 | - CommandList_struct *cmpQ; | |
205 | + struct hlist_head reqQ; | |
206 | + struct hlist_head cmpQ; | |
207 | unsigned int Qdepth; | |
208 | unsigned int maxQsinceinit; | |
209 | unsigned int maxSG; | |
210 | diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h | |
211 | index 43bf559..e4ca588 100644 | |
212 | --- a/drivers/block/cciss_cmd.h | |
213 | +++ b/drivers/block/cciss_cmd.h | |
214 | @@ -249,6 +249,7 @@ typedef struct _ErrorInfo_struct { | |
215 | #define CMD_SCSI 0x03 | |
216 | #define CMD_MSG_DONE 0x04 | |
217 | #define CMD_MSG_TIMEOUT 0x05 | |
218 | +#define CMD_MSG_STALE 0xff | |
219 | ||
220 | /* This structure needs to be divisible by 8 for new | |
221 | * indexing method. | |
222 | @@ -265,8 +266,7 @@ typedef struct _CommandList_struct { | |
223 | int ctlr; | |
224 | int cmd_type; | |
225 | long cmdindex; | |
226 | - struct _CommandList_struct *prev; | |
227 | - struct _CommandList_struct *next; | |
228 | + struct hlist_node list; | |
229 | struct request * rq; | |
230 | struct completion *waiting; | |
231 | int retry_count; |