]>
Commit | Line | Data |
---|---|---|
c8806b6c NM |
1 | /* |
2 | * Copyright 2014 Cisco Systems, Inc. All rights reserved. | |
3 | * | |
4 | * This program is free software; you may redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; version 2 of the License. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
9 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
10 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
11 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
12 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
15 | * SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include <linux/errno.h> | |
19 | #include <linux/mempool.h> | |
20 | ||
21 | #include <scsi/scsi_tcq.h> | |
22 | ||
23 | #include "snic_disc.h" | |
24 | #include "snic.h" | |
25 | #include "snic_io.h" | |
26 | ||
27 | ||
28 | /* snic target types */ | |
29 | static const char * const snic_tgt_type_str[] = { | |
30 | [SNIC_TGT_DAS] = "DAS", | |
31 | [SNIC_TGT_SAN] = "SAN", | |
32 | }; | |
33 | ||
34 | static inline const char * | |
35 | snic_tgt_type_to_str(int typ) | |
36 | { | |
37 | return ((typ > SNIC_TGT_NONE && typ <= SNIC_TGT_SAN) ? | |
38 | snic_tgt_type_str[typ] : "Unknown"); | |
39 | } | |
40 | ||
41 | static const char * const snic_tgt_state_str[] = { | |
42 | [SNIC_TGT_STAT_INIT] = "INIT", | |
43 | [SNIC_TGT_STAT_ONLINE] = "ONLINE", | |
44 | [SNIC_TGT_STAT_OFFLINE] = "OFFLINE", | |
45 | [SNIC_TGT_STAT_DEL] = "DELETION IN PROGRESS", | |
46 | }; | |
47 | ||
48 | const char * | |
49 | snic_tgt_state_to_str(int state) | |
50 | { | |
51 | return ((state >= SNIC_TGT_STAT_INIT && state <= SNIC_TGT_STAT_DEL) ? | |
52 | snic_tgt_state_str[state] : "UNKNOWN"); | |
53 | } | |
54 | ||
55 | /* | |
56 | * Initiate report_tgt req desc | |
57 | */ | |
58 | static void | |
59 | snic_report_tgt_init(struct snic_host_req *req, u32 hid, u8 *buf, u32 len, | |
60 | dma_addr_t rsp_buf_pa, ulong ctx) | |
61 | { | |
62 | struct snic_sg_desc *sgd = NULL; | |
63 | ||
64 | ||
65 | snic_io_hdr_enc(&req->hdr, SNIC_REQ_REPORT_TGTS, 0, SCSI_NO_TAG, hid, | |
66 | 1, ctx); | |
67 | ||
68 | req->u.rpt_tgts.sg_cnt = cpu_to_le16(1); | |
69 | sgd = req_to_sgl(req); | |
70 | sgd[0].addr = cpu_to_le64(rsp_buf_pa); | |
71 | sgd[0].len = cpu_to_le32(len); | |
72 | sgd[0]._resvd = 0; | |
73 | req->u.rpt_tgts.sg_addr = cpu_to_le64((ulong)sgd); | |
74 | } | |
75 | ||
76 | /* | |
77 | * snic_queue_report_tgt_req: Queues report target request. | |
78 | */ | |
79 | static int | |
80 | snic_queue_report_tgt_req(struct snic *snic) | |
81 | { | |
82 | struct snic_req_info *rqi = NULL; | |
83 | u32 ntgts, buf_len = 0; | |
84 | u8 *buf = NULL; | |
85 | dma_addr_t pa = 0; | |
86 | int ret = 0; | |
87 | ||
88 | rqi = snic_req_init(snic, 1); | |
89 | if (!rqi) { | |
90 | ret = -ENOMEM; | |
91 | goto error; | |
92 | } | |
93 | ||
94 | if (snic->fwinfo.max_tgts) | |
95 | ntgts = min_t(u32, snic->fwinfo.max_tgts, snic->shost->max_id); | |
96 | else | |
97 | ntgts = snic->shost->max_id; | |
98 | ||
99 | /* Allocate Response Buffer */ | |
100 | SNIC_BUG_ON(ntgts == 0); | |
101 | buf_len = ntgts * sizeof(struct snic_tgt_id) + SNIC_SG_DESC_ALIGN; | |
102 | ||
103 | buf = kzalloc(buf_len, GFP_KERNEL|GFP_DMA); | |
104 | if (!buf) { | |
105 | snic_req_free(snic, rqi); | |
106 | SNIC_HOST_ERR(snic->shost, "Resp Buf Alloc Failed.\n"); | |
107 | ||
108 | ret = -ENOMEM; | |
109 | goto error; | |
110 | } | |
111 | ||
112 | SNIC_BUG_ON((((unsigned long)buf) % SNIC_SG_DESC_ALIGN) != 0); | |
113 | ||
114 | pa = pci_map_single(snic->pdev, buf, buf_len, PCI_DMA_FROMDEVICE); | |
115 | if (pci_dma_mapping_error(snic->pdev, pa)) { | |
116 | kfree(buf); | |
117 | snic_req_free(snic, rqi); | |
118 | SNIC_HOST_ERR(snic->shost, | |
119 | "Rpt-tgt rspbuf %p: PCI DMA Mapping Failed\n", | |
120 | buf); | |
121 | ret = -EINVAL; | |
122 | ||
123 | goto error; | |
124 | } | |
125 | ||
126 | ||
127 | SNIC_BUG_ON(pa == 0); | |
128 | rqi->sge_va = (ulong) buf; | |
129 | ||
130 | snic_report_tgt_init(rqi->req, | |
131 | snic->config.hid, | |
132 | buf, | |
133 | buf_len, | |
134 | pa, | |
135 | (ulong)rqi); | |
136 | ||
137 | snic_handle_untagged_req(snic, rqi); | |
138 | ||
139 | ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len); | |
140 | if (ret) { | |
141 | pci_unmap_single(snic->pdev, pa, buf_len, PCI_DMA_FROMDEVICE); | |
142 | kfree(buf); | |
143 | rqi->sge_va = 0; | |
144 | snic_release_untagged_req(snic, rqi); | |
145 | SNIC_HOST_ERR(snic->shost, "Queuing Report Tgts Failed.\n"); | |
146 | ||
147 | goto error; | |
148 | } | |
149 | ||
150 | SNIC_DISC_DBG(snic->shost, "Report Targets Issued.\n"); | |
151 | ||
152 | return ret; | |
153 | ||
154 | error: | |
155 | SNIC_HOST_ERR(snic->shost, | |
156 | "Queuing Report Targets Failed, err = %d\n", | |
157 | ret); | |
158 | return ret; | |
159 | } /* end of snic_queue_report_tgt_req */ | |
160 | ||
161 | /* call into SML */ | |
162 | static void | |
163 | snic_scsi_scan_tgt(struct work_struct *work) | |
164 | { | |
165 | struct snic_tgt *tgt = container_of(work, struct snic_tgt, scan_work); | |
166 | struct Scsi_Host *shost = dev_to_shost(&tgt->dev); | |
167 | unsigned long flags; | |
168 | ||
169 | SNIC_HOST_INFO(shost, "Scanning Target id 0x%x\n", tgt->id); | |
170 | scsi_scan_target(&tgt->dev, | |
171 | tgt->channel, | |
172 | tgt->scsi_tgt_id, | |
173 | SCAN_WILD_CARD, | |
1d645088 | 174 | SCSI_SCAN_RESCAN); |
c8806b6c NM |
175 | |
176 | spin_lock_irqsave(shost->host_lock, flags); | |
177 | tgt->flags &= ~SNIC_TGT_SCAN_PENDING; | |
178 | spin_unlock_irqrestore(shost->host_lock, flags); | |
179 | } /* end of snic_scsi_scan_tgt */ | |
180 | ||
181 | /* | |
182 | * snic_tgt_lookup : | |
183 | */ | |
184 | static struct snic_tgt * | |
185 | snic_tgt_lookup(struct snic *snic, struct snic_tgt_id *tgtid) | |
186 | { | |
187 | struct list_head *cur, *nxt; | |
188 | struct snic_tgt *tgt = NULL; | |
189 | ||
190 | list_for_each_safe(cur, nxt, &snic->disc.tgt_list) { | |
191 | tgt = list_entry(cur, struct snic_tgt, list); | |
192 | if (tgt->id == le32_to_cpu(tgtid->tgt_id)) | |
193 | return tgt; | |
194 | tgt = NULL; | |
195 | } | |
196 | ||
197 | return tgt; | |
198 | } /* end of snic_tgt_lookup */ | |
199 | ||
200 | /* | |
201 | * snic_tgt_dev_release : Called on dropping last ref for snic_tgt object | |
202 | */ | |
203 | void | |
204 | snic_tgt_dev_release(struct device *dev) | |
205 | { | |
206 | struct snic_tgt *tgt = dev_to_tgt(dev); | |
207 | ||
208 | SNIC_HOST_INFO(snic_tgt_to_shost(tgt), | |
209 | "Target Device ID %d (%s) Permanently Deleted.\n", | |
210 | tgt->id, | |
211 | dev_name(dev)); | |
212 | ||
213 | SNIC_BUG_ON(!list_empty(&tgt->list)); | |
214 | kfree(tgt); | |
215 | } | |
216 | ||
217 | /* | |
218 | * snic_tgt_del : work function to delete snic_tgt | |
219 | */ | |
220 | static void | |
221 | snic_tgt_del(struct work_struct *work) | |
222 | { | |
223 | struct snic_tgt *tgt = container_of(work, struct snic_tgt, del_work); | |
224 | struct Scsi_Host *shost = snic_tgt_to_shost(tgt); | |
225 | ||
226 | if (tgt->flags & SNIC_TGT_SCAN_PENDING) | |
227 | scsi_flush_work(shost); | |
228 | ||
229 | /* Block IOs on child devices, stops new IOs */ | |
230 | scsi_target_block(&tgt->dev); | |
231 | ||
232 | /* Cleanup IOs */ | |
233 | snic_tgt_scsi_abort_io(tgt); | |
234 | ||
235 | /* Unblock IOs now, to flush if there are any. */ | |
236 | scsi_target_unblock(&tgt->dev, SDEV_TRANSPORT_OFFLINE); | |
237 | ||
238 | /* Delete SCSI Target and sdevs */ | |
239 | scsi_remove_target(&tgt->dev); /* ?? */ | |
240 | device_del(&tgt->dev); | |
241 | put_device(&tgt->dev); | |
242 | } /* end of snic_tgt_del */ | |
243 | ||
244 | /* snic_tgt_create: checks for existence of snic_tgt, if it doesn't | |
245 | * it creates one. | |
246 | */ | |
247 | static struct snic_tgt * | |
248 | snic_tgt_create(struct snic *snic, struct snic_tgt_id *tgtid) | |
249 | { | |
250 | struct snic_tgt *tgt = NULL; | |
251 | unsigned long flags; | |
252 | int ret; | |
253 | ||
254 | tgt = snic_tgt_lookup(snic, tgtid); | |
255 | if (tgt) { | |
256 | /* update the information if required */ | |
257 | return tgt; | |
258 | } | |
259 | ||
260 | tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); | |
261 | if (!tgt) { | |
262 | SNIC_HOST_ERR(snic->shost, "Failure to allocate snic_tgt.\n"); | |
263 | ret = -ENOMEM; | |
264 | ||
265 | return tgt; | |
266 | } | |
267 | ||
268 | INIT_LIST_HEAD(&tgt->list); | |
269 | tgt->id = le32_to_cpu(tgtid->tgt_id); | |
270 | tgt->channel = 0; | |
271 | ||
272 | SNIC_BUG_ON(le16_to_cpu(tgtid->tgt_type) > SNIC_TGT_SAN); | |
273 | tgt->tdata.typ = le16_to_cpu(tgtid->tgt_type); | |
274 | ||
275 | /* | |
276 | * Plugging into SML Device Tree | |
277 | */ | |
278 | tgt->tdata.disc_id = 0; | |
279 | tgt->state = SNIC_TGT_STAT_INIT; | |
280 | device_initialize(&tgt->dev); | |
281 | tgt->dev.parent = get_device(&snic->shost->shost_gendev); | |
282 | tgt->dev.release = snic_tgt_dev_release; | |
283 | INIT_WORK(&tgt->scan_work, snic_scsi_scan_tgt); | |
284 | INIT_WORK(&tgt->del_work, snic_tgt_del); | |
285 | switch (tgt->tdata.typ) { | |
286 | case SNIC_TGT_DAS: | |
287 | dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d", | |
288 | snic->shost->host_no, tgt->channel, tgt->id); | |
289 | break; | |
290 | ||
291 | case SNIC_TGT_SAN: | |
292 | dev_set_name(&tgt->dev, "snic_san_tgt:%d:%d-%d", | |
293 | snic->shost->host_no, tgt->channel, tgt->id); | |
294 | break; | |
295 | ||
296 | default: | |
297 | SNIC_HOST_INFO(snic->shost, "Target type Unknown Detected.\n"); | |
298 | dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d", | |
299 | snic->shost->host_no, tgt->channel, tgt->id); | |
300 | break; | |
301 | } | |
302 | ||
303 | spin_lock_irqsave(snic->shost->host_lock, flags); | |
304 | list_add_tail(&tgt->list, &snic->disc.tgt_list); | |
305 | tgt->scsi_tgt_id = snic->disc.nxt_tgt_id++; | |
306 | tgt->state = SNIC_TGT_STAT_ONLINE; | |
307 | spin_unlock_irqrestore(snic->shost->host_lock, flags); | |
308 | ||
309 | SNIC_HOST_INFO(snic->shost, | |
310 | "Tgt %d, type = %s detected. Adding..\n", | |
311 | tgt->id, snic_tgt_type_to_str(tgt->tdata.typ)); | |
312 | ||
313 | ret = device_add(&tgt->dev); | |
314 | if (ret) { | |
315 | SNIC_HOST_ERR(snic->shost, | |
316 | "Snic Tgt: device_add, with err = %d\n", | |
317 | ret); | |
318 | ||
319 | put_device(&snic->shost->shost_gendev); | |
320 | kfree(tgt); | |
321 | tgt = NULL; | |
322 | ||
323 | return tgt; | |
324 | } | |
325 | ||
326 | SNIC_HOST_INFO(snic->shost, "Scanning %s.\n", dev_name(&tgt->dev)); | |
327 | ||
328 | scsi_queue_work(snic->shost, &tgt->scan_work); | |
329 | ||
330 | return tgt; | |
331 | } /* end of snic_tgt_create */ | |
332 | ||
333 | /* Handler for discovery */ | |
334 | void | |
335 | snic_handle_tgt_disc(struct work_struct *work) | |
336 | { | |
337 | struct snic *snic = container_of(work, struct snic, tgt_work); | |
338 | struct snic_tgt_id *tgtid = NULL; | |
339 | struct snic_tgt *tgt = NULL; | |
340 | unsigned long flags; | |
341 | int i; | |
342 | ||
343 | spin_lock_irqsave(&snic->snic_lock, flags); | |
344 | if (snic->in_remove) { | |
345 | spin_unlock_irqrestore(&snic->snic_lock, flags); | |
346 | kfree(snic->disc.rtgt_info); | |
347 | ||
348 | return; | |
349 | } | |
350 | spin_unlock_irqrestore(&snic->snic_lock, flags); | |
351 | ||
352 | mutex_lock(&snic->disc.mutex); | |
353 | /* Discover triggered during disc in progress */ | |
354 | if (snic->disc.req_cnt) { | |
355 | snic->disc.state = SNIC_DISC_DONE; | |
356 | snic->disc.req_cnt = 0; | |
357 | mutex_unlock(&snic->disc.mutex); | |
358 | kfree(snic->disc.rtgt_info); | |
359 | snic->disc.rtgt_info = NULL; | |
360 | ||
361 | SNIC_HOST_INFO(snic->shost, "tgt_disc: Discovery restart.\n"); | |
362 | /* Start Discovery Again */ | |
363 | snic_disc_start(snic); | |
364 | ||
365 | return; | |
366 | } | |
367 | ||
368 | tgtid = (struct snic_tgt_id *)snic->disc.rtgt_info; | |
369 | ||
370 | SNIC_BUG_ON(snic->disc.rtgt_cnt == 0 || tgtid == NULL); | |
371 | ||
372 | for (i = 0; i < snic->disc.rtgt_cnt; i++) { | |
373 | tgt = snic_tgt_create(snic, &tgtid[i]); | |
374 | if (!tgt) { | |
375 | int buf_sz = snic->disc.rtgt_cnt * sizeof(*tgtid); | |
376 | ||
377 | SNIC_HOST_ERR(snic->shost, "Failed to create tgt.\n"); | |
378 | snic_hex_dump("rpt_tgt_rsp", (char *)tgtid, buf_sz); | |
379 | break; | |
380 | } | |
381 | } | |
382 | ||
383 | snic->disc.rtgt_info = NULL; | |
384 | snic->disc.state = SNIC_DISC_DONE; | |
385 | mutex_unlock(&snic->disc.mutex); | |
386 | ||
387 | SNIC_HOST_INFO(snic->shost, "Discovery Completed.\n"); | |
388 | ||
389 | kfree(tgtid); | |
390 | } /* end of snic_handle_tgt_disc */ | |
391 | ||
392 | ||
393 | int | |
394 | snic_report_tgt_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) | |
395 | { | |
396 | ||
397 | u8 typ, cmpl_stat; | |
398 | u32 cmnd_id, hid, tgt_cnt = 0; | |
399 | ulong ctx; | |
400 | struct snic_req_info *rqi = NULL; | |
401 | struct snic_tgt_id *tgtid; | |
402 | int i, ret = 0; | |
403 | ||
404 | snic_io_hdr_dec(&fwreq->hdr, &typ, &cmpl_stat, &cmnd_id, &hid, &ctx); | |
405 | rqi = (struct snic_req_info *) ctx; | |
406 | tgtid = (struct snic_tgt_id *) rqi->sge_va; | |
407 | ||
408 | tgt_cnt = le32_to_cpu(fwreq->u.rpt_tgts_cmpl.tgt_cnt); | |
409 | if (tgt_cnt == 0) { | |
410 | SNIC_HOST_ERR(snic->shost, "No Targets Found on this host.\n"); | |
411 | ret = 1; | |
412 | ||
413 | goto end; | |
414 | } | |
415 | ||
416 | /* printing list of targets here */ | |
417 | SNIC_HOST_INFO(snic->shost, "Target Count = %d\n", tgt_cnt); | |
418 | ||
419 | SNIC_BUG_ON(tgt_cnt > snic->fwinfo.max_tgts); | |
420 | ||
421 | for (i = 0; i < tgt_cnt; i++) | |
422 | SNIC_HOST_INFO(snic->shost, | |
423 | "Tgt id = 0x%x\n", | |
424 | le32_to_cpu(tgtid[i].tgt_id)); | |
425 | ||
426 | /* | |
427 | * Queue work for further processing, | |
428 | * Response Buffer Memory is freed after creating targets | |
429 | */ | |
430 | snic->disc.rtgt_cnt = tgt_cnt; | |
431 | snic->disc.rtgt_info = (u8 *) tgtid; | |
432 | queue_work(snic_glob->event_q, &snic->tgt_work); | |
433 | ret = 0; | |
434 | ||
435 | end: | |
436 | /* Unmap Response Buffer */ | |
437 | snic_pci_unmap_rsp_buf(snic, rqi); | |
438 | if (ret) | |
439 | kfree(tgtid); | |
440 | ||
441 | rqi->sge_va = 0; | |
442 | snic_release_untagged_req(snic, rqi); | |
443 | ||
444 | return ret; | |
445 | } /* end of snic_report_tgt_cmpl_handler */ | |
446 | ||
447 | /* Discovery init fn */ | |
448 | void | |
449 | snic_disc_init(struct snic_disc *disc) | |
450 | { | |
451 | INIT_LIST_HEAD(&disc->tgt_list); | |
452 | mutex_init(&disc->mutex); | |
453 | disc->disc_id = 0; | |
454 | disc->nxt_tgt_id = 0; | |
455 | disc->state = SNIC_DISC_INIT; | |
456 | disc->req_cnt = 0; | |
457 | disc->rtgt_cnt = 0; | |
458 | disc->rtgt_info = NULL; | |
459 | disc->cb = NULL; | |
460 | } /* end of snic_disc_init */ | |
461 | ||
462 | /* Discovery, uninit fn */ | |
463 | void | |
464 | snic_disc_term(struct snic *snic) | |
465 | { | |
466 | struct snic_disc *disc = &snic->disc; | |
467 | ||
468 | mutex_lock(&disc->mutex); | |
469 | if (disc->req_cnt) { | |
470 | disc->req_cnt = 0; | |
471 | SNIC_SCSI_DBG(snic->shost, "Terminating Discovery.\n"); | |
472 | } | |
473 | mutex_unlock(&disc->mutex); | |
474 | } | |
475 | ||
476 | /* | |
477 | * snic_disc_start: Discovery Start ... | |
478 | */ | |
479 | int | |
480 | snic_disc_start(struct snic *snic) | |
481 | { | |
482 | struct snic_disc *disc = &snic->disc; | |
483 | int ret = 0; | |
484 | ||
485 | SNIC_SCSI_DBG(snic->shost, "Discovery Start.\n"); | |
486 | ||
487 | mutex_lock(&disc->mutex); | |
488 | if (disc->state == SNIC_DISC_PENDING) { | |
489 | disc->req_cnt++; | |
490 | mutex_unlock(&disc->mutex); | |
491 | ||
492 | return ret; | |
493 | } | |
494 | disc->state = SNIC_DISC_PENDING; | |
495 | mutex_unlock(&disc->mutex); | |
496 | ||
497 | ret = snic_queue_report_tgt_req(snic); | |
498 | if (ret) | |
499 | SNIC_HOST_INFO(snic->shost, "Discovery Failed, err=%d.\n", ret); | |
500 | ||
501 | return ret; | |
502 | } /* end of snic_disc_start */ | |
503 | ||
504 | /* | |
505 | * snic_disc_work : | |
506 | */ | |
507 | void | |
508 | snic_handle_disc(struct work_struct *work) | |
509 | { | |
510 | struct snic *snic = container_of(work, struct snic, disc_work); | |
511 | int ret = 0; | |
512 | ||
513 | SNIC_HOST_INFO(snic->shost, "disc_work: Discovery\n"); | |
514 | ||
515 | ret = snic_disc_start(snic); | |
516 | if (ret) | |
517 | goto disc_err; | |
518 | ||
519 | disc_err: | |
520 | SNIC_HOST_ERR(snic->shost, | |
521 | "disc_work: Discovery Failed w/ err = %d\n", | |
522 | ret); | |
523 | } /* end of snic_disc_work */ | |
524 | ||
525 | /* | |
526 | * snic_tgt_del_all : cleanup all snic targets | |
527 | * Called on unbinding the interface | |
528 | */ | |
529 | void | |
530 | snic_tgt_del_all(struct snic *snic) | |
531 | { | |
532 | struct snic_tgt *tgt = NULL; | |
533 | struct list_head *cur, *nxt; | |
534 | unsigned long flags; | |
535 | ||
536 | mutex_lock(&snic->disc.mutex); | |
537 | spin_lock_irqsave(snic->shost->host_lock, flags); | |
538 | ||
539 | list_for_each_safe(cur, nxt, &snic->disc.tgt_list) { | |
540 | tgt = list_entry(cur, struct snic_tgt, list); | |
541 | tgt->state = SNIC_TGT_STAT_DEL; | |
542 | list_del_init(&tgt->list); | |
543 | SNIC_HOST_INFO(snic->shost, "Tgt %d q'ing for del\n", tgt->id); | |
544 | queue_work(snic_glob->event_q, &tgt->del_work); | |
545 | tgt = NULL; | |
546 | } | |
547 | spin_unlock_irqrestore(snic->shost->host_lock, flags); | |
548 | ||
549 | scsi_flush_work(snic->shost); | |
550 | mutex_unlock(&snic->disc.mutex); | |
551 | } /* end of snic_tgt_del_all */ |