diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2006-11-16 05:24:13 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-11-25 14:17:18 -0500 |
commit | 5a55c2596f55e3a60f7502d0dfcfa0d20241e5ac (patch) | |
tree | 30bee283b134036d67b15e597270f3718a5e41e5 /drivers/scsi | |
parent | b58d91547fb17c65ad621f3f98b1f2c228c812a5 (diff) |
[SCSI] scsi tgt: scsi target lib functionality
The core scsi target lib functions.
TODO:
- mv md/dm-bio-list.h to linux/bio-list.h so md and us do not have to
do that weird include.
- convert scsi_tgt_cmd's work struct to James's execute code. And try
to kill our scsi_tgt_cmd.
- add host state checking. We do refcouting so hotplug is partially
supported, but we need to add state checking to make it easier on
the LLD.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/scsi_tgt_lib.c | 742 | ||||
-rw-r--r-- | drivers/scsi/scsi_tgt_priv.h | 25 |
2 files changed, 767 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c new file mode 100644 index 000000000000..39da5cd6fb6f --- /dev/null +++ b/drivers/scsi/scsi_tgt_lib.c | |||
@@ -0,0 +1,742 @@ | |||
1 | /* | ||
2 | * SCSI target lib functions | ||
3 | * | ||
4 | * Copyright (C) 2005 Mike Christie <michaelc@cs.wisc.edu> | ||
5 | * Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of the | ||
10 | * License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | */ | ||
22 | #include <linux/blkdev.h> | ||
23 | #include <linux/hash.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/pagemap.h> | ||
26 | #include <scsi/scsi.h> | ||
27 | #include <scsi/scsi_cmnd.h> | ||
28 | #include <scsi/scsi_device.h> | ||
29 | #include <scsi/scsi_host.h> | ||
30 | #include <scsi/scsi_tgt.h> | ||
31 | #include <../drivers/md/dm-bio-list.h> | ||
32 | |||
33 | #include "scsi_tgt_priv.h" | ||
34 | |||
35 | static struct workqueue_struct *scsi_tgtd; | ||
36 | static kmem_cache_t *scsi_tgt_cmd_cache; | ||
37 | |||
38 | /* | ||
39 | * TODO: this struct will be killed when the block layer supports large bios | ||
40 | * and James's work struct code is in | ||
41 | */ | ||
42 | struct scsi_tgt_cmd { | ||
43 | /* TODO replace work with James b's code */ | ||
44 | struct work_struct work; | ||
45 | /* TODO replace the lists with a large bio */ | ||
46 | struct bio_list xfer_done_list; | ||
47 | struct bio_list xfer_list; | ||
48 | |||
49 | struct list_head hash_list; | ||
50 | struct request *rq; | ||
51 | u64 tag; | ||
52 | |||
53 | void *buffer; | ||
54 | unsigned bufflen; | ||
55 | }; | ||
56 | |||
57 | #define TGT_HASH_ORDER 4 | ||
58 | #define cmd_hashfn(tag) hash_long((unsigned long) (tag), TGT_HASH_ORDER) | ||
59 | |||
60 | struct scsi_tgt_queuedata { | ||
61 | struct Scsi_Host *shost; | ||
62 | struct list_head cmd_hash[1 << TGT_HASH_ORDER]; | ||
63 | spinlock_t cmd_hash_lock; | ||
64 | }; | ||
65 | |||
66 | /* | ||
67 | * Function: scsi_host_get_command() | ||
68 | * | ||
69 | * Purpose: Allocate and setup a scsi command block and blk request | ||
70 | * | ||
71 | * Arguments: shost - scsi host | ||
72 | * data_dir - dma data dir | ||
73 | * gfp_mask- allocator flags | ||
74 | * | ||
75 | * Returns: The allocated scsi command structure. | ||
76 | * | ||
77 | * This should be called by target LLDs to get a command. | ||
78 | */ | ||
79 | struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost, | ||
80 | enum dma_data_direction data_dir, | ||
81 | gfp_t gfp_mask) | ||
82 | { | ||
83 | int write = (data_dir == DMA_TO_DEVICE); | ||
84 | struct request *rq; | ||
85 | struct scsi_cmnd *cmd; | ||
86 | struct scsi_tgt_cmd *tcmd; | ||
87 | |||
88 | /* Bail if we can't get a reference to the device */ | ||
89 | if (!get_device(&shost->shost_gendev)) | ||
90 | return NULL; | ||
91 | |||
92 | tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC); | ||
93 | if (!tcmd) | ||
94 | goto put_dev; | ||
95 | |||
96 | rq = blk_get_request(shost->uspace_req_q, write, gfp_mask); | ||
97 | if (!rq) | ||
98 | goto free_tcmd; | ||
99 | |||
100 | cmd = __scsi_get_command(shost, gfp_mask); | ||
101 | if (!cmd) | ||
102 | goto release_rq; | ||
103 | |||
104 | memset(cmd, 0, sizeof(*cmd)); | ||
105 | cmd->sc_data_direction = data_dir; | ||
106 | cmd->jiffies_at_alloc = jiffies; | ||
107 | cmd->request = rq; | ||
108 | |||
109 | rq->special = cmd; | ||
110 | rq->cmd_type = REQ_TYPE_SPECIAL; | ||
111 | rq->cmd_flags |= REQ_TYPE_BLOCK_PC; | ||
112 | rq->end_io_data = tcmd; | ||
113 | |||
114 | bio_list_init(&tcmd->xfer_list); | ||
115 | bio_list_init(&tcmd->xfer_done_list); | ||
116 | tcmd->rq = rq; | ||
117 | |||
118 | return cmd; | ||
119 | |||
120 | release_rq: | ||
121 | blk_put_request(rq); | ||
122 | free_tcmd: | ||
123 | kmem_cache_free(scsi_tgt_cmd_cache, tcmd); | ||
124 | put_dev: | ||
125 | put_device(&shost->shost_gendev); | ||
126 | return NULL; | ||
127 | |||
128 | } | ||
129 | EXPORT_SYMBOL_GPL(scsi_host_get_command); | ||
130 | |||
131 | /* | ||
132 | * Function: scsi_host_put_command() | ||
133 | * | ||
134 | * Purpose: Free a scsi command block | ||
135 | * | ||
136 | * Arguments: shost - scsi host | ||
137 | * cmd - command block to free | ||
138 | * | ||
139 | * Returns: Nothing. | ||
140 | * | ||
141 | * Notes: The command must not belong to any lists. | ||
142 | */ | ||
143 | void scsi_host_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) | ||
144 | { | ||
145 | struct request_queue *q = shost->uspace_req_q; | ||
146 | struct request *rq = cmd->request; | ||
147 | struct scsi_tgt_cmd *tcmd = rq->end_io_data; | ||
148 | unsigned long flags; | ||
149 | |||
150 | kmem_cache_free(scsi_tgt_cmd_cache, tcmd); | ||
151 | |||
152 | spin_lock_irqsave(q->queue_lock, flags); | ||
153 | __blk_put_request(q, rq); | ||
154 | spin_unlock_irqrestore(q->queue_lock, flags); | ||
155 | |||
156 | __scsi_put_command(shost, cmd, &shost->shost_gendev); | ||
157 | } | ||
158 | EXPORT_SYMBOL_GPL(scsi_host_put_command); | ||
159 | |||
160 | static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd) | ||
161 | { | ||
162 | struct bio *bio; | ||
163 | |||
164 | /* must call bio_endio in case bio was bounced */ | ||
165 | while ((bio = bio_list_pop(&tcmd->xfer_done_list))) { | ||
166 | bio_endio(bio, bio->bi_size, 0); | ||
167 | bio_unmap_user(bio); | ||
168 | } | ||
169 | |||
170 | while ((bio = bio_list_pop(&tcmd->xfer_list))) { | ||
171 | bio_endio(bio, bio->bi_size, 0); | ||
172 | bio_unmap_user(bio); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static void cmd_hashlist_del(struct scsi_cmnd *cmd) | ||
177 | { | ||
178 | struct request_queue *q = cmd->request->q; | ||
179 | struct scsi_tgt_queuedata *qdata = q->queuedata; | ||
180 | unsigned long flags; | ||
181 | struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; | ||
182 | |||
183 | spin_lock_irqsave(&qdata->cmd_hash_lock, flags); | ||
184 | list_del(&tcmd->hash_list); | ||
185 | spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); | ||
186 | } | ||
187 | |||
188 | static void scsi_tgt_cmd_destroy(void *data) | ||
189 | { | ||
190 | struct scsi_cmnd *cmd = data; | ||
191 | struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; | ||
192 | |||
193 | dprintk("cmd %p %d %lu\n", cmd, cmd->sc_data_direction, | ||
194 | rq_data_dir(cmd->request)); | ||
195 | /* | ||
196 | * We fix rq->cmd_flags here since when we told bio_map_user | ||
197 | * to write vm for WRITE commands, blk_rq_bio_prep set | ||
198 | * rq_data_dir the flags to READ. | ||
199 | */ | ||
200 | if (cmd->sc_data_direction == DMA_TO_DEVICE) | ||
201 | cmd->request->cmd_flags |= REQ_RW; | ||
202 | else | ||
203 | cmd->request->cmd_flags &= ~REQ_RW; | ||
204 | |||
205 | scsi_unmap_user_pages(tcmd); | ||
206 | scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd); | ||
207 | } | ||
208 | |||
209 | static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd, | ||
210 | u64 tag) | ||
211 | { | ||
212 | struct scsi_tgt_queuedata *qdata = rq->q->queuedata; | ||
213 | unsigned long flags; | ||
214 | struct list_head *head; | ||
215 | |||
216 | tcmd->tag = tag; | ||
217 | spin_lock_irqsave(&qdata->cmd_hash_lock, flags); | ||
218 | head = &qdata->cmd_hash[cmd_hashfn(tag)]; | ||
219 | list_add(&tcmd->hash_list, head); | ||
220 | spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * scsi_tgt_alloc_queue - setup queue used for message passing | ||
225 | * shost: scsi host | ||
226 | * | ||
227 | * This should be called by the LLD after host allocation. | ||
228 | * And will be released when the host is released. | ||
229 | */ | ||
230 | int scsi_tgt_alloc_queue(struct Scsi_Host *shost) | ||
231 | { | ||
232 | struct scsi_tgt_queuedata *queuedata; | ||
233 | struct request_queue *q; | ||
234 | int err, i; | ||
235 | |||
236 | /* | ||
237 | * Do we need to send a netlink event or should uspace | ||
238 | * just respond to the hotplug event? | ||
239 | */ | ||
240 | q = __scsi_alloc_queue(shost, NULL); | ||
241 | if (!q) | ||
242 | return -ENOMEM; | ||
243 | |||
244 | queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL); | ||
245 | if (!queuedata) { | ||
246 | err = -ENOMEM; | ||
247 | goto cleanup_queue; | ||
248 | } | ||
249 | queuedata->shost = shost; | ||
250 | q->queuedata = queuedata; | ||
251 | |||
252 | /* | ||
253 | * this is a silly hack. We should probably just queue as many | ||
254 | * command as is recvd to userspace. uspace can then make | ||
255 | * sure we do not overload the HBA | ||
256 | */ | ||
257 | q->nr_requests = shost->hostt->can_queue; | ||
258 | /* | ||
259 | * We currently only support software LLDs so this does | ||
260 | * not matter for now. Do we need this for the cards we support? | ||
261 | * If so we should make it a host template value. | ||
262 | */ | ||
263 | blk_queue_dma_alignment(q, 0); | ||
264 | shost->uspace_req_q = q; | ||
265 | |||
266 | for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++) | ||
267 | INIT_LIST_HEAD(&queuedata->cmd_hash[i]); | ||
268 | spin_lock_init(&queuedata->cmd_hash_lock); | ||
269 | |||
270 | return 0; | ||
271 | |||
272 | cleanup_queue: | ||
273 | blk_cleanup_queue(q); | ||
274 | return err; | ||
275 | } | ||
276 | EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue); | ||
277 | |||
278 | void scsi_tgt_free_queue(struct Scsi_Host *shost) | ||
279 | { | ||
280 | int i; | ||
281 | unsigned long flags; | ||
282 | struct request_queue *q = shost->uspace_req_q; | ||
283 | struct scsi_cmnd *cmd; | ||
284 | struct scsi_tgt_queuedata *qdata = q->queuedata; | ||
285 | struct scsi_tgt_cmd *tcmd, *n; | ||
286 | LIST_HEAD(cmds); | ||
287 | |||
288 | spin_lock_irqsave(&qdata->cmd_hash_lock, flags); | ||
289 | |||
290 | for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) { | ||
291 | list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i], | ||
292 | hash_list) { | ||
293 | list_del(&tcmd->hash_list); | ||
294 | list_add(&tcmd->hash_list, &cmds); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); | ||
299 | |||
300 | while (!list_empty(&cmds)) { | ||
301 | tcmd = list_entry(cmds.next, struct scsi_tgt_cmd, hash_list); | ||
302 | list_del(&tcmd->hash_list); | ||
303 | cmd = tcmd->rq->special; | ||
304 | |||
305 | shost->hostt->eh_abort_handler(cmd); | ||
306 | scsi_tgt_cmd_destroy(cmd); | ||
307 | } | ||
308 | } | ||
309 | EXPORT_SYMBOL_GPL(scsi_tgt_free_queue); | ||
310 | |||
311 | struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd) | ||
312 | { | ||
313 | struct scsi_tgt_queuedata *queue = cmd->request->q->queuedata; | ||
314 | return queue->shost; | ||
315 | } | ||
316 | EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host); | ||
317 | |||
318 | /* | ||
319 | * scsi_tgt_queue_command - queue command for userspace processing | ||
320 | * @cmd: scsi command | ||
321 | * @scsilun: scsi lun | ||
322 | * @tag: unique value to identify this command for tmf | ||
323 | */ | ||
324 | int scsi_tgt_queue_command(struct scsi_cmnd *cmd, struct scsi_lun *scsilun, | ||
325 | u64 tag) | ||
326 | { | ||
327 | struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; | ||
328 | int err; | ||
329 | |||
330 | init_scsi_tgt_cmd(cmd->request, tcmd, tag); | ||
331 | err = scsi_tgt_uspace_send_cmd(cmd, scsilun, tag); | ||
332 | if (err) | ||
333 | cmd_hashlist_del(cmd); | ||
334 | |||
335 | return err; | ||
336 | } | ||
337 | EXPORT_SYMBOL_GPL(scsi_tgt_queue_command); | ||
338 | |||
339 | /* | ||
340 | * This is run from a interrpt handler normally and the unmap | ||
341 | * needs process context so we must queue | ||
342 | */ | ||
343 | static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd) | ||
344 | { | ||
345 | struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; | ||
346 | |||
347 | dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); | ||
348 | |||
349 | scsi_tgt_uspace_send_status(cmd, tcmd->tag); | ||
350 | INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy, cmd); | ||
351 | queue_work(scsi_tgtd, &tcmd->work); | ||
352 | } | ||
353 | |||
354 | static int __scsi_tgt_transfer_response(struct scsi_cmnd *cmd) | ||
355 | { | ||
356 | struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); | ||
357 | int err; | ||
358 | |||
359 | dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); | ||
360 | |||
361 | err = shost->hostt->transfer_response(cmd, scsi_tgt_cmd_done); | ||
362 | switch (err) { | ||
363 | case SCSI_MLQUEUE_HOST_BUSY: | ||
364 | case SCSI_MLQUEUE_DEVICE_BUSY: | ||
365 | return -EAGAIN; | ||
366 | } | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static void scsi_tgt_transfer_response(struct scsi_cmnd *cmd) | ||
372 | { | ||
373 | struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; | ||
374 | int err; | ||
375 | |||
376 | err = __scsi_tgt_transfer_response(cmd); | ||
377 | if (!err) | ||
378 | return; | ||
379 | |||
380 | cmd->result = DID_BUS_BUSY << 16; | ||
381 | err = scsi_tgt_uspace_send_status(cmd, tcmd->tag); | ||
382 | if (err <= 0) | ||
383 | /* the eh will have to pick this up */ | ||
384 | printk(KERN_ERR "Could not send cmd %p status\n", cmd); | ||
385 | } | ||
386 | |||
387 | static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask) | ||
388 | { | ||
389 | struct request *rq = cmd->request; | ||
390 | struct scsi_tgt_cmd *tcmd = rq->end_io_data; | ||
391 | int count; | ||
392 | |||
393 | cmd->use_sg = rq->nr_phys_segments; | ||
394 | cmd->request_buffer = scsi_alloc_sgtable(cmd, gfp_mask); | ||
395 | if (!cmd->request_buffer) | ||
396 | return -ENOMEM; | ||
397 | |||
398 | cmd->request_bufflen = rq->data_len; | ||
399 | |||
400 | dprintk("cmd %p addr %p cnt %d %lu\n", cmd, tcmd->buffer, cmd->use_sg, | ||
401 | rq_data_dir(rq)); | ||
402 | count = blk_rq_map_sg(rq->q, rq, cmd->request_buffer); | ||
403 | if (likely(count <= cmd->use_sg)) { | ||
404 | cmd->use_sg = count; | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | eprintk("cmd %p addr %p cnt %d\n", cmd, tcmd->buffer, cmd->use_sg); | ||
409 | scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); | ||
410 | return -EINVAL; | ||
411 | } | ||
412 | |||
413 | /* TODO: test this crap and replace bio_map_user with new interface maybe */ | ||
414 | static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, | ||
415 | int rw) | ||
416 | { | ||
417 | struct request_queue *q = cmd->request->q; | ||
418 | struct request *rq = cmd->request; | ||
419 | void *uaddr = tcmd->buffer; | ||
420 | unsigned int len = tcmd->bufflen; | ||
421 | struct bio *bio; | ||
422 | int err; | ||
423 | |||
424 | while (len > 0) { | ||
425 | dprintk("%lx %u\n", (unsigned long) uaddr, len); | ||
426 | bio = bio_map_user(q, NULL, (unsigned long) uaddr, len, rw); | ||
427 | if (IS_ERR(bio)) { | ||
428 | err = PTR_ERR(bio); | ||
429 | dprintk("fail to map %lx %u %d %x\n", | ||
430 | (unsigned long) uaddr, len, err, cmd->cmnd[0]); | ||
431 | goto unmap_bios; | ||
432 | } | ||
433 | |||
434 | uaddr += bio->bi_size; | ||
435 | len -= bio->bi_size; | ||
436 | |||
437 | /* | ||
438 | * The first bio is added and merged. We could probably | ||
439 | * try to add others using scsi_merge_bio() but for now | ||
440 | * we keep it simple. The first bio should be pretty large | ||
441 | * (either hitting the 1 MB bio pages limit or a queue limit) | ||
442 | * already but for really large IO we may want to try and | ||
443 | * merge these. | ||
444 | */ | ||
445 | if (!rq->bio) { | ||
446 | blk_rq_bio_prep(q, rq, bio); | ||
447 | rq->data_len = bio->bi_size; | ||
448 | } else | ||
449 | /* put list of bios to transfer in next go around */ | ||
450 | bio_list_add(&tcmd->xfer_list, bio); | ||
451 | } | ||
452 | |||
453 | cmd->offset = 0; | ||
454 | err = scsi_tgt_init_cmd(cmd, GFP_KERNEL); | ||
455 | if (err) | ||
456 | goto unmap_bios; | ||
457 | |||
458 | return 0; | ||
459 | |||
460 | unmap_bios: | ||
461 | if (rq->bio) { | ||
462 | bio_unmap_user(rq->bio); | ||
463 | while ((bio = bio_list_pop(&tcmd->xfer_list))) | ||
464 | bio_unmap_user(bio); | ||
465 | } | ||
466 | |||
467 | return err; | ||
468 | } | ||
469 | |||
470 | static int scsi_tgt_transfer_data(struct scsi_cmnd *); | ||
471 | |||
472 | static void scsi_tgt_data_transfer_done(struct scsi_cmnd *cmd) | ||
473 | { | ||
474 | struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; | ||
475 | struct bio *bio; | ||
476 | int err; | ||
477 | |||
478 | /* should we free resources here on error ? */ | ||
479 | if (cmd->result) { | ||
480 | send_uspace_err: | ||
481 | err = scsi_tgt_uspace_send_status(cmd, tcmd->tag); | ||
482 | if (err <= 0) | ||
483 | /* the tgt uspace eh will have to pick this up */ | ||
484 | printk(KERN_ERR "Could not send cmd %p status\n", cmd); | ||
485 | return; | ||
486 | } | ||
487 | |||
488 | dprintk("cmd %p request_bufflen %u bufflen %u\n", | ||
489 | cmd, cmd->request_bufflen, tcmd->bufflen); | ||
490 | |||
491 | scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); | ||
492 | bio_list_add(&tcmd->xfer_done_list, cmd->request->bio); | ||
493 | |||
494 | tcmd->buffer += cmd->request_bufflen; | ||
495 | cmd->offset += cmd->request_bufflen; | ||
496 | |||
497 | if (!tcmd->xfer_list.head) { | ||
498 | scsi_tgt_transfer_response(cmd); | ||
499 | return; | ||
500 | } | ||
501 | |||
502 | dprintk("cmd2 %p request_bufflen %u bufflen %u\n", | ||
503 | cmd, cmd->request_bufflen, tcmd->bufflen); | ||
504 | |||
505 | bio = bio_list_pop(&tcmd->xfer_list); | ||
506 | BUG_ON(!bio); | ||
507 | |||
508 | blk_rq_bio_prep(cmd->request->q, cmd->request, bio); | ||
509 | cmd->request->data_len = bio->bi_size; | ||
510 | err = scsi_tgt_init_cmd(cmd, GFP_ATOMIC); | ||
511 | if (err) { | ||
512 | cmd->result = DID_ERROR << 16; | ||
513 | goto send_uspace_err; | ||
514 | } | ||
515 | |||
516 | if (scsi_tgt_transfer_data(cmd)) { | ||
517 | cmd->result = DID_NO_CONNECT << 16; | ||
518 | goto send_uspace_err; | ||
519 | } | ||
520 | } | ||
521 | |||
522 | static int scsi_tgt_transfer_data(struct scsi_cmnd *cmd) | ||
523 | { | ||
524 | int err; | ||
525 | struct Scsi_Host *host = scsi_tgt_cmd_to_host(cmd); | ||
526 | |||
527 | err = host->hostt->transfer_data(cmd, scsi_tgt_data_transfer_done); | ||
528 | switch (err) { | ||
529 | case SCSI_MLQUEUE_HOST_BUSY: | ||
530 | case SCSI_MLQUEUE_DEVICE_BUSY: | ||
531 | return -EAGAIN; | ||
532 | default: | ||
533 | return 0; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr, | ||
538 | unsigned len) | ||
539 | { | ||
540 | char __user *p = (char __user *) uaddr; | ||
541 | |||
542 | if (copy_from_user(cmd->sense_buffer, p, | ||
543 | min_t(unsigned, SCSI_SENSE_BUFFERSIZE, len))) { | ||
544 | printk(KERN_ERR "Could not copy the sense buffer\n"); | ||
545 | return -EIO; | ||
546 | } | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int scsi_tgt_abort_cmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd) | ||
551 | { | ||
552 | int err; | ||
553 | |||
554 | err = shost->hostt->eh_abort_handler(cmd); | ||
555 | if (err) | ||
556 | eprintk("fail to abort %p\n", cmd); | ||
557 | |||
558 | scsi_tgt_cmd_destroy(cmd); | ||
559 | return err; | ||
560 | } | ||
561 | |||
562 | static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u64 tag) | ||
563 | { | ||
564 | struct scsi_tgt_queuedata *qdata = q->queuedata; | ||
565 | struct request *rq = NULL; | ||
566 | struct list_head *head; | ||
567 | struct scsi_tgt_cmd *tcmd; | ||
568 | unsigned long flags; | ||
569 | |||
570 | head = &qdata->cmd_hash[cmd_hashfn(tag)]; | ||
571 | spin_lock_irqsave(&qdata->cmd_hash_lock, flags); | ||
572 | list_for_each_entry(tcmd, head, hash_list) { | ||
573 | if (tcmd->tag == tag) { | ||
574 | rq = tcmd->rq; | ||
575 | list_del(&tcmd->hash_list); | ||
576 | break; | ||
577 | } | ||
578 | } | ||
579 | spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); | ||
580 | |||
581 | return rq; | ||
582 | } | ||
583 | |||
584 | int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, | ||
585 | unsigned long uaddr, u8 rw) | ||
586 | { | ||
587 | struct Scsi_Host *shost; | ||
588 | struct scsi_cmnd *cmd; | ||
589 | struct request *rq; | ||
590 | struct scsi_tgt_cmd *tcmd; | ||
591 | int err = 0; | ||
592 | |||
593 | dprintk("%d %llu %d %u %lx %u\n", host_no, (unsigned long long) tag, | ||
594 | result, len, uaddr, rw); | ||
595 | |||
596 | /* TODO: replace with a O(1) alg */ | ||
597 | shost = scsi_host_lookup(host_no); | ||
598 | if (IS_ERR(shost)) { | ||
599 | printk(KERN_ERR "Could not find host no %d\n", host_no); | ||
600 | return -EINVAL; | ||
601 | } | ||
602 | |||
603 | if (!shost->uspace_req_q) { | ||
604 | printk(KERN_ERR "Not target scsi host %d\n", host_no); | ||
605 | goto done; | ||
606 | } | ||
607 | |||
608 | rq = tgt_cmd_hash_lookup(shost->uspace_req_q, tag); | ||
609 | if (!rq) { | ||
610 | printk(KERN_ERR "Could not find tag %llu\n", | ||
611 | (unsigned long long) tag); | ||
612 | err = -EINVAL; | ||
613 | goto done; | ||
614 | } | ||
615 | cmd = rq->special; | ||
616 | |||
617 | dprintk("cmd %p result %d len %d bufflen %u %lu %x\n", cmd, | ||
618 | result, len, cmd->request_bufflen, rq_data_dir(rq), cmd->cmnd[0]); | ||
619 | |||
620 | if (result == TASK_ABORTED) { | ||
621 | scsi_tgt_abort_cmd(shost, cmd); | ||
622 | goto done; | ||
623 | } | ||
624 | /* | ||
625 | * store the userspace values here, the working values are | ||
626 | * in the request_* values | ||
627 | */ | ||
628 | tcmd = cmd->request->end_io_data; | ||
629 | tcmd->buffer = (void *)uaddr; | ||
630 | tcmd->bufflen = len; | ||
631 | cmd->result = result; | ||
632 | |||
633 | if (!tcmd->bufflen || cmd->request_buffer) { | ||
634 | err = __scsi_tgt_transfer_response(cmd); | ||
635 | goto done; | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * TODO: Do we need to handle case where request does not | ||
640 | * align with LLD. | ||
641 | */ | ||
642 | err = scsi_map_user_pages(rq->end_io_data, cmd, rw); | ||
643 | if (err) { | ||
644 | eprintk("%p %d\n", cmd, err); | ||
645 | err = -EAGAIN; | ||
646 | goto done; | ||
647 | } | ||
648 | |||
649 | /* userspace failure */ | ||
650 | if (cmd->result) { | ||
651 | if (status_byte(cmd->result) == CHECK_CONDITION) | ||
652 | scsi_tgt_copy_sense(cmd, uaddr, len); | ||
653 | err = __scsi_tgt_transfer_response(cmd); | ||
654 | goto done; | ||
655 | } | ||
656 | /* ask the target LLD to transfer the data to the buffer */ | ||
657 | err = scsi_tgt_transfer_data(cmd); | ||
658 | |||
659 | done: | ||
660 | scsi_host_put(shost); | ||
661 | return err; | ||
662 | } | ||
663 | |||
664 | int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, int function, u64 tag, | ||
665 | struct scsi_lun *scsilun, void *data) | ||
666 | { | ||
667 | int err; | ||
668 | |||
669 | /* TODO: need to retry if this fails. */ | ||
670 | err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, function, | ||
671 | tag, scsilun, data); | ||
672 | if (err < 0) | ||
673 | eprintk("The task management request lost!\n"); | ||
674 | return err; | ||
675 | } | ||
676 | EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request); | ||
677 | |||
678 | int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result) | ||
679 | { | ||
680 | struct Scsi_Host *shost; | ||
681 | int err = -EINVAL; | ||
682 | |||
683 | dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid); | ||
684 | |||
685 | shost = scsi_host_lookup(host_no); | ||
686 | if (IS_ERR(shost)) { | ||
687 | printk(KERN_ERR "Could not find host no %d\n", host_no); | ||
688 | return err; | ||
689 | } | ||
690 | |||
691 | if (!shost->uspace_req_q) { | ||
692 | printk(KERN_ERR "Not target scsi host %d\n", host_no); | ||
693 | goto done; | ||
694 | } | ||
695 | |||
696 | err = shost->hostt->tsk_mgmt_response(mid, result); | ||
697 | done: | ||
698 | scsi_host_put(shost); | ||
699 | return err; | ||
700 | } | ||
701 | |||
702 | static int __init scsi_tgt_init(void) | ||
703 | { | ||
704 | int err; | ||
705 | |||
706 | scsi_tgt_cmd_cache = kmem_cache_create("scsi_tgt_cmd", | ||
707 | sizeof(struct scsi_tgt_cmd), | ||
708 | 0, 0, NULL, NULL); | ||
709 | if (!scsi_tgt_cmd_cache) | ||
710 | return -ENOMEM; | ||
711 | |||
712 | scsi_tgtd = create_workqueue("scsi_tgtd"); | ||
713 | if (!scsi_tgtd) { | ||
714 | err = -ENOMEM; | ||
715 | goto free_kmemcache; | ||
716 | } | ||
717 | |||
718 | err = scsi_tgt_if_init(); | ||
719 | if (err) | ||
720 | goto destroy_wq; | ||
721 | |||
722 | return 0; | ||
723 | |||
724 | destroy_wq: | ||
725 | destroy_workqueue(scsi_tgtd); | ||
726 | free_kmemcache: | ||
727 | kmem_cache_destroy(scsi_tgt_cmd_cache); | ||
728 | return err; | ||
729 | } | ||
730 | |||
731 | static void __exit scsi_tgt_exit(void) | ||
732 | { | ||
733 | destroy_workqueue(scsi_tgtd); | ||
734 | scsi_tgt_if_exit(); | ||
735 | kmem_cache_destroy(scsi_tgt_cmd_cache); | ||
736 | } | ||
737 | |||
738 | module_init(scsi_tgt_init); | ||
739 | module_exit(scsi_tgt_exit); | ||
740 | |||
741 | MODULE_DESCRIPTION("SCSI target core"); | ||
742 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h new file mode 100644 index 000000000000..84488c51ff62 --- /dev/null +++ b/drivers/scsi/scsi_tgt_priv.h | |||
@@ -0,0 +1,25 @@ | |||
1 | struct scsi_cmnd; | ||
2 | struct scsi_lun; | ||
3 | struct Scsi_Host; | ||
4 | struct task_struct; | ||
5 | |||
6 | /* tmp - will replace with SCSI logging stuff */ | ||
7 | #define eprintk(fmt, args...) \ | ||
8 | do { \ | ||
9 | printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ | ||
10 | } while (0) | ||
11 | |||
12 | #define dprintk(fmt, args...) | ||
13 | /* #define dprintk eprintk */ | ||
14 | |||
15 | extern void scsi_tgt_if_exit(void); | ||
16 | extern int scsi_tgt_if_init(void); | ||
17 | |||
18 | extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun, | ||
19 | u64 tag); | ||
20 | extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 tag); | ||
21 | extern int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, | ||
22 | unsigned long uaddr, u8 rw); | ||
23 | extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, | ||
24 | struct scsi_lun *scsilun, void *data); | ||
25 | extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result); | ||