GIT 0df298d180556450cbe5edf12c1e890f6ac6ea97 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-target-2.6.git commit 0df298d180556450cbe5edf12c1e890f6ac6ea97 Author: James Bottomley Date: Sat May 20 20:33:26 2006 -0500 [SCSI] scsi tgt: correct Scsi_Host definition Fix simple compile error Signed-off-by: James Bottomley commit c4864e32e2c96300d329e08543e815d634d7799e Author: FUJITA Tomonori Date: Sat Apr 29 15:59:37 2006 +0900 [SCSI] scsi tgt: ibmvstgt driver This is IBM Virtual SCSI target driver for tgt. This patch dependents on the previous patch to convert the ibmvscsi driver to use include/scsi/srp.h. The driver is based on the original ibmvscsis driver: http://lkml.org/lkml/2005/10/17/99 Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit 42733440aaf7ad0cff23c1bc53242eef35e058d9 Author: FUJITA Tomonori Date: Sat Apr 29 15:59:29 2006 +0900 [SCSI] scsi tgt: libsrp libsrp provides helper functions for SRP target drivers. The next step would be to add initiator support to merge the two SRP initiator drivers (ibmvscsi and ib_srp) in mainline. Some SRP target drivers would be out of drivers/scsi/ so I added an entry for libsrp in drivers/scsi/Kconfig, though I'm not sure about it. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit 051efd8717e4a7d049fe091e9dacd65ab11ac40b Author: FUJITA Tomonori Date: Mon Apr 10 10:49:15 2006 +0900 [SCSI] scsi tgt: add NET dependence to Kconfig > From: "Jun'ichi Nomura" : scsi_tgt_if.c depends on CONFIG_NET for using netlink. So it would be nice if the Kconfig entry checks it. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit b6ad18a75d90adc18c4e724e2b74e33a63b3495e Author: FUJITA Tomonori Date: Mon Apr 10 10:49:15 2006 +0900 [SCSI] scsi tgt: add task management function support This patch addes task management function support to tgt. - add callback to task management function to scsi_host_template structure. It is used notify LLDs of the completion of a TMF request. - this patch doesn't use a single queue for TMF requests and SCSI commands yet. We'll work on it later on. - when LLDs queue scsi commands to tgt (scsi_tgt_queue_command), they need to specify unique 'tag' for each command for ABORT_TASK. - when tgt aborts a command, it calls eh_abort_handler in scsi_host_template structure. Would be better to add tgt_eh_abort_handler for LLDs support target and initiator modes at the same time? tgt TMF works in the followings: - When LLDs queue scsi commands to tgt (scsi_tgt_queue_command), they need to specify unique 'tag' for each command. - LLDs call 'int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *host, int, u64 tag, struct scsi_lun *lun, void *data)'. - int (* tsk_mgmt_response)(u64 data, int result) is added to scsi_host_template. When an initiator sends a task management request, the LLD calls scsi_tgt_tsk_mgmt_request. the LLD can use whatever it wants for the data arg. The data arg is used later as the arg in the tsk_mgmt_response callback. tgt core just sends the task management request to user space (by using TGT_KEVENT_TSK_MGMT_REQ). In the case of ABORT_TASK, tgtd finds a single command to abort and sends TGT_UEVENT_CMD_RSP and TGT_UEVENT_TSK_MGMT_RSP events. tgt core calls eh_abort_handler for TGT_UEVENT_CMD_RSP and then tsk_mgmt_response for TGT_UEVENT_TSK_MGMT_RSP. If tgtd fails to find a command to abort, it sends only TGT_UEVENT_TSK_MGMT_RSP event (no TGT_UEVENT_CMD_RSP event). In the case of the rests task management function (like ABORT_TASK_SET), tgt needs to abort multiple commands. Thus, tgtd finds multiple commands to abort and sends multiple TGT_UEVENT_CMD_RSP events and a single TGT_UEVENT_TSK_MGMT_RSP event. tgt core calls eh_abort_handler multiple times and tsk_mgmt_response once. eh_abort_handler enables LLDs to safely free resource related with a command to abort. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit 1b407894be6586a33b96a8a3f7a6ae0807041b92 Author: FUJITA Tomonori Date: Mon Apr 10 10:49:15 2006 +0900 [SCSI] scsi tgt: replace the elevator code tgt uses the elevator code to send SCSI commands to the user-space daemon (q->request_fn sends netlink packets including commands). This patch replaces the elevator code with a simple list. This is mainly because tgt also needs to send TMF requests to the user-space daemon (the daemon does all the SCSI state machine stuff). tgt must send SCSI commands and TMF requests in an exact order so that it would be preferable to use a single queue (per host) for both. To uses the elevator code for TMF requests, tgt needs to allocate request structures for them. That's wasteful because request structures is useless for TMF requests, which don't perform any I/Os. We basically have a netdev queue of events to send to userspace so by using the request_queue and netdev queue we are basically double queueing and wasting resources and it is affecting performance We like to use shared memory stuff between kernel and user spaces instead of netlink in the future. These queues would go away. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit ff6a475b45c87196a11e14063a08546f5d3aca20 Author: FUJITA Tomonori Date: Mon Apr 10 10:49:14 2006 +0900 [SCSI] scsi tgt: remove blk_queue_end_tag Remove blk_queue_end_tag() in scsi_host_put_command() because tgt doesn't use the elevator code. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit ee190a69e69d21b09e914955aad8ecb21a063c3d Author: FUJITA Tomonori Date: Mon Apr 10 10:49:14 2006 +0900 [SCSI] scsi tgt: fix double lock in scsi_uspace_request_fn Fix double lock in scsi_uspace_request_fn. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit aa7a9d141599cf31e9a3ca9cd2a17d7b69779a18 Author: FUJITA Tomonori Date: Mon Apr 10 10:49:14 2006 +0900 [SCSI] scsi tgt: kernel/user interface changes Simplify the tgt kernel/user interface. - merge the tgt command structure with the the event structure for simplicity. - add a new event type for task management. - remove some of unused event types. - send task attributes to user-space daemon. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit e0cbd21b79c80ab30a0121aeeec30b95936b85a3 Author: FUJITA Tomonori Date: Mon Apr 10 10:49:14 2006 +0900 [SCSI] block layer: use blk_rq_bio_prep in init_request_from_bio Patch to use blk_rq_bio_prep in init_request_from_bio. And remove blk_rq_bio_prep's flags copying. The first three bits have not been the same for some time so that has been broken. The user of blk_rq_bio_prep will setup the request flags so if it wanted failfast or to be a barrier it will set the correct flag itself. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit 1361c1dc6b969b9f954b27bb04f3182a330ec687 Author: FUJITA Tomonori Date: Mon Apr 10 10:49:14 2006 +0900 [SCSI] scsi tgt: use the original bio_map_user interface Return to the original bio_map_user interface. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit 469765a95699713c891a439e5766b5fe9a8d1e4d Author: FUJITA Tomonori Date: Mon Apr 10 10:49:13 2006 +0900 [SCSI] block layer: add partial mappings support to bio_map_user This is the updated patch for partial mappings support. - bio_map_user_iov always allows partial mappings. - The two users (blk_rq_map_user and blk_rq_map_user_iov) will fails if the bio is partially mapped. - Added a length argument to blk_rq_map_user_iov in order to avoid including sg.h in ll_rw_blk.c for struct sg_iovec. This is a resend: http://marc.theaimsgroup.com/?l=linux-scsi&m=114086655400806&w=2 Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit 2507d72d882fcd01bee1db4cf33c9250af8091a1 Author: FUJITA Tomonori Date: Mon Apr 10 10:49:13 2006 +0900 [SCSI] block layer: revoke the original patch to add partial mappings support For target mode we could end up with the case where we get very large request from the initiator. The request could be so large that we cannot transfer all the data in one operation. For example the HBA's segment or max_sector limits might limit us to a 1 MB transfer. To send a 5 MB command then we need to transfer the command chunk by chunk. To do this, tgt core will map in as much data as possible into a bio, send this off, then when that transfer is completed we send off another request/bio. To be able to pack as much data into a bio as possible we need bio_map_user to support partially mapped bios. Following the comments from Jens on the original patch: http://marc.theaimsgroup.com/?l=linux-scsi&m=114012008928530&w=2 This patch will revoke changes by the original patch. Signed-off-by: FUJITA Tomonori Signed-off-by: Mike Christie Signed-off-by: James Bottomley commit fb9fe0de7bac3f121ab27879450a9e3b150fb760 Author: Mike Christie Date: Thu Feb 16 13:53:47 2006 -0600 [SCSI] scsi-ml: Makefile and Kconfig changes for stgt Makefile and Kconfig stuff. Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley commit e046a8bcb393ab0cad254b37a23fc7452bc1c878 Author: Mike Christie Date: Thu Feb 16 13:53:44 2006 -0600 [SCSI] scsi tgt: scsi target netlink interface This patch implments a netlink interface for the scsi tgt framework. I was not sure if code using the netlink interface had to get reviewed by the netdev guys. I am ccing them on this patch and providing a basic review of why/how we want to use netlink. I did not think the netdev people wanted to see the scsi and block layer code, so I am just sending the netlink interface part of this patchset to netdev. I can resend the other parts if needed. The scsi tgt framework, adds support for scsi target mode cards. So instead of using the scsi card in your box as a initiator/host you can use it as a target/server. The reason for the netlink use is becuase the target normally receives a interrupt indicating that a command or event is ready to be processed. The scsi card's driver will then call a scsi lib function which eventually calls scsi_tgt_uspace_send (in this patch below) to tell userspace to begin to process the request (userspace contains the state model). Later userspace will call back into the kernel by sending a netlink msg, and instruct the scsi driver what to do next. When the scsi driver is done executing the operation, it will send a netlink message back to userspace to indicate the success or failure of the operation (using scsi_tgt_uspace_send_status in the patch below). Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley commit 23dc00aa854892679800d84327e82f5abdbd2bcf Author: Mike Christie Date: Thu Feb 16 13:53:42 2006 -0600 [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. - make it so the request_queue can be used to pass around these target messages better (see todo in code), or maybe just remove request_queue usage all together and use our own linked_list or something else. We currently use the queue for tag numbers so if we remove the request_queue we will have to add some sort of host tag list like was suggested for iscsi. We also use the queue to store the HBA limits and build proper sized bios and reqeusts so we would need a shell queue like what dm uses. - eh handling (still in the process of working on proper state model in userspace). - must remove our request->flags hack Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley commit e1b0867bb5841aa02171f72fc04b5b04a0023d32 Author: Mike Christie Date: Thu Feb 16 13:53:40 2006 -0600 [SCSI] block layer: add partial mappings support to bio_map_user For target mode we could end up with the case where we get very large request from the initiator. The request could be so large that we cannot transfer all the data in one operation. For example the HBA's segment or max_sector limits might limit us to a 1 MB transfer. To send a 5 MB command then we need to transfer the command chunk by chunk. To do this, tgt core will map in as much data as possible into a bio, send this off, then when that transfer is completed we send off another request/bio. To be able to pack as much data into a bio as possible we need bio_map_user to support partially mapped bios. The attached patch just adds a new argument to the those functions and if set will not return a failure if the bio is partially mapped. Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley commit a3fd43d6d640686a7d74152743aa4915b7416e92 Author: Mike Christie Date: Thu Feb 16 13:53:37 2006 -0600 [SCSI] block layer: kill length alignment test in bin_map_user The tgt project is mapping in bios using bio_map_user. The current targets do not need their len to be aligned with a queue limit so this check is causing some problems. Note: pointers passed into the kernel are properly aligned by usersapace tgt code so the uaddr check in bio_map_user is ok. The major user, blk_bio_map_user checks for the len before mapping so it is not affected by this patch. And the semi-newly added user blk_rq_map_user_iov has been failing out when the len is not aligned properly so maybe people have been good and not sending misaligned lens or that path is not used very often and this change will not be very dangerous. st and sg do not check the length and we have not seen any problem reports from those wider used paths so this patch should be fairly safe - for mm and wider testing at least. Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley commit f933212dee6729448f4bbfd8d3f7a110156b1511 Author: Mike Christie Date: Thu Feb 16 13:53:35 2006 -0600 [SCSI] scsi-ml: export scsi-ml functions needed by tgt_scsi_lib and its LLDs This patch contains the needed changes to the scsi-ml to support targets. Note, per the last review we moved almost all the fields we added to the scsi_cmnd to our internal data structure which we are going to try and kill off when we can replace it with support from other parts of the kernel. The one field we left on was the offset variable. This is needed to handle the case where the target gets request that is so large that it cannot execute it in one dma operation. So max_secotors or a segment limit may limit the size of the transfer. In this case our tgt core code will break up the command into managable transfers and send them to the LLD one at a time. The offset is then used to tell the LLD where in the command we are at. Is there another field on the scsi_cmd for that? Signed-off-by: Mike Christie Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- Signed-off-by: Andrew Morton --- block/ll_rw_blk.c | 40 - block/scsi_ioctl.c | 3 drivers/scsi/Kconfig | 30 + drivers/scsi/Makefile | 5 drivers/scsi/hosts.c | 5 drivers/scsi/ibmvscsi/Makefile | 2 drivers/scsi/libsrp.c | 449 ++++++++++++++++++++ drivers/scsi/scsi.c | 89 ++++ drivers/scsi/scsi_lib.c | 33 + drivers/scsi/scsi_tgt_if.c | 235 ++++++++++ drivers/scsi/scsi_tgt_lib.c | 667 +++++++++++++++++++++++++++++++ drivers/scsi/scsi_tgt_priv.h | 24 + fs/bio.c | 19 include/linux/blkdev.h | 3 include/linux/netlink.h | 1 include/scsi/libsrp.h | 75 +++ include/scsi/scsi_cmnd.h | 3 include/scsi/scsi_host.h | 43 + include/scsi/scsi_tgt.h | 13 include/scsi/scsi_tgt_if.h | 89 ++++ 20 files changed, 1781 insertions(+), 47 deletions(-) diff -puN block/ll_rw_blk.c~git-scsi-target block/ll_rw_blk.c --- devel/block/ll_rw_blk.c~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/block/ll_rw_blk.c 2006-05-21 14:45:50.000000000 -0700 @@ -2354,19 +2354,20 @@ int blk_rq_map_user(request_queue_t *q, else bio = bio_copy_user(q, uaddr, len, reading); - if (!IS_ERR(bio)) { - rq->bio = rq->biotail = bio; - blk_rq_bio_prep(q, rq, bio); + if (IS_ERR(bio)) + return PTR_ERR(bio); - rq->buffer = rq->data = NULL; - rq->data_len = len; - return 0; + if (bio->bi_size != len) { + bio_endio(bio, bio->bi_size, 0); + bio_unmap_user(bio); + return -EINVAL; } - /* - * bio is the err-ptr - */ - return PTR_ERR(bio); + rq->bio = rq->biotail = bio; + blk_rq_bio_prep(q, rq, bio); + rq->buffer = rq->data = NULL; + rq->data_len = len; + return 0; } EXPORT_SYMBOL(blk_rq_map_user); @@ -2392,7 +2393,7 @@ EXPORT_SYMBOL(blk_rq_map_user); * unmapping. */ int blk_rq_map_user_iov(request_queue_t *q, struct request *rq, - struct sg_iovec *iov, int iov_count) + struct sg_iovec *iov, int iov_count, unsigned int len) { struct bio *bio; @@ -2406,6 +2407,12 @@ int blk_rq_map_user_iov(request_queue_t if (IS_ERR(bio)) return PTR_ERR(bio); + if (bio->bi_size != len) { + bio_endio(bio, bio->bi_size, 0); + bio_unmap_user(bio); + return -EINVAL; + } + rq->bio = rq->biotail = bio; blk_rq_bio_prep(q, rq, bio); rq->buffer = rq->data = NULL; @@ -2829,16 +2836,12 @@ static void init_request_from_bio(struct req->errors = 0; req->hard_sector = req->sector = bio->bi_sector; - req->hard_nr_sectors = req->nr_sectors = bio_sectors(bio); - req->current_nr_sectors = req->hard_cur_sectors = bio_cur_sectors(bio); - req->nr_phys_segments = bio_phys_segments(req->q, bio); - req->nr_hw_segments = bio_hw_segments(req->q, bio); - req->buffer = bio_data(bio); /* see ->buffer comment above */ req->waiting = NULL; - req->bio = req->biotail = bio; req->ioprio = bio_prio(bio); req->rq_disk = bio->bi_bdev->bd_disk; req->start_time = jiffies; + + blk_rq_bio_prep(req->q, req, bio); } static int __make_request(request_queue_t *q, struct bio *bio) @@ -3485,9 +3488,6 @@ EXPORT_SYMBOL(end_request); void blk_rq_bio_prep(request_queue_t *q, struct request *rq, struct bio *bio) { - /* first three bits are identical in rq->flags and bio->bi_rw */ - rq->flags |= (bio->bi_rw & 7); - rq->nr_phys_segments = bio_phys_segments(q, bio); rq->nr_hw_segments = bio_hw_segments(q, bio); rq->current_nr_sectors = bio_cur_sectors(bio); diff -puN block/scsi_ioctl.c~git-scsi-target block/scsi_ioctl.c --- devel/block/scsi_ioctl.c~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/block/scsi_ioctl.c 2006-05-21 14:45:50.000000000 -0700 @@ -274,7 +274,8 @@ static int sg_io(struct file *file, requ goto out; } - ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count); + ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count, + hdr->dxfer_len); kfree(iov); } else if (hdr->dxfer_len) ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); diff -puN drivers/scsi/hosts.c~git-scsi-target drivers/scsi/hosts.c --- devel/drivers/scsi/hosts.c~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/drivers/scsi/hosts.c 2006-05-21 14:45:50.000000000 -0700 @@ -264,6 +264,11 @@ static void scsi_host_dev_release(struct if (shost->work_q) destroy_workqueue(shost->work_q); + if (shost->uspace_req_q) { + kfree(shost->uspace_req_q->queuedata); + scsi_free_queue(shost->uspace_req_q); + } + scsi_destroy_command_freelist(shost); kfree(shost->shost_data); diff -puN drivers/scsi/ibmvscsi/Makefile~git-scsi-target drivers/scsi/ibmvscsi/Makefile --- devel/drivers/scsi/ibmvscsi/Makefile~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/drivers/scsi/ibmvscsi/Makefile 2006-05-21 14:45:50.000000000 -0700 @@ -3,3 +3,5 @@ obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic ibmvscsic-y += ibmvscsi.o ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o + +obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvstgt.o diff -puN drivers/scsi/Kconfig~git-scsi-target drivers/scsi/Kconfig --- devel/drivers/scsi/Kconfig~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/drivers/scsi/Kconfig 2006-05-21 14:45:50.000000000 -0700 @@ -27,6 +27,13 @@ config SCSI However, do not compile this as a module if your root file system (the one containing the directory /) is located on a SCSI device. +config SCSI_TGT + tristate "SCSI target support" + depends on SCSI && NET && EXPERIMENTAL + ---help--- + If you want to use SCSI target mode drivers enable this option. + If you choose M, the module will be called scsi_tgt. + config SCSI_PROC_FS bool "legacy /proc/scsi/ support" depends on SCSI && PROC_FS @@ -985,6 +992,20 @@ config SCSI_IBMVSCSI To compile this driver as a module, choose M here: the module will be called ibmvscsic. +config SCSI_IBMVSCSIS + tristate "IBM Virtual SCSI Server support" + depends on PPC_PSERIES && SCSI_TGT && SCSI_SRP + help + This is the SRP target driver for IBM pSeries virtual environments. + + The userspace component needed to initialize the driver and + documentation can be found: + + http://stgt.berlios.de/ + + To compile this driver as a module, choose M here: the + module will be called ibmvstgt. + config SCSI_INITIO tristate "Initio 9100U(W) support" depends on PCI && SCSI @@ -1914,6 +1935,15 @@ config ZFCP called zfcp. If you want to compile it as a module, say M here and read . +config SCSI_SRP + tristate "SCSI RDMA Protocol helper library" + depends on SCSI + help + If you wish to use SRP target drivers, say Y. + + To compile this driver as a module, choose M here: the + module will be called libsrp. + endmenu source "drivers/scsi/pcmcia/Kconfig" diff -puN /dev/null drivers/scsi/libsrp.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/scsi/libsrp.c 2006-05-21 14:45:50.000000000 -0700 @@ -0,0 +1,449 @@ +/* + * SCSI RDAM Protocol lib functions + * + * Copyright (C) 2006 FUJITA Tomonori + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum srp_task_attributes { + SRP_SIMPLE_TASK = 0, + SRP_HEAD_TASK = 1, + SRP_ORDERED_TASK = 2, + SRP_ACA_TASK = 4 +}; + +/* tmp - will replace with SCSI logging stuff */ +#define eprintk(fmt, args...) \ +do { \ + printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ +} while (0) +/* #define dprintk eprintk */ +#define dprintk(fmt, args...) + +static int srp_iu_pool_alloc(struct srp_queue *q, size_t max, + struct srp_buf **ring) +{ + int i; + struct iu_entry *iue; + + q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL); + if (!q->pool) + return -ENOMEM; + q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL); + if (!q->items) + goto free_pool; + + spin_lock_init(&q->lock); + q->queue = kfifo_init((void *) q->pool, max * sizeof(void *), + GFP_KERNEL, &q->lock); + if (IS_ERR(q->queue)) + goto free_item; + + for (i = 0, iue = q->items; i < max; i++) { + __kfifo_put(q->queue, (void *) &iue, sizeof(void *)); + iue->sbuf = ring[i]; + iue++; + } + return 0; + +free_item: + kfree(q->items); +free_pool: + kfree(q->pool); + return -ENOMEM; +} + +static void srp_iu_pool_free(struct srp_queue *q) +{ + kfree(q->items); + kfree(q->pool); +} + +static struct srp_buf ** srp_ring_alloc(struct device *dev, + size_t max, size_t size) +{ + int i; + struct srp_buf **ring; + + ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL); + if (!ring) + return NULL; + + for (i = 0; i < max; i++) { + ring[i] = kzalloc(sizeof(struct srp_buf), GFP_KERNEL); + if (!ring[i]) + goto out; + ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma, + GFP_KERNEL); + if (!ring[i]->buf) + goto out; + } + return ring; + +out: + for (i = 0; i < max && ring[i]; i++) { + if (ring[i]->buf) + dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma); + kfree(ring[i]); + } + kfree(ring); + + return NULL; +} + +static void srp_ring_free(struct device *dev, struct srp_buf **ring, size_t max, + size_t size) +{ + int i; + + for (i = 0; i < max; i++) { + dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma); + kfree(ring[i]); + } +} + +int srp_target_alloc(struct srp_target *target, struct device *dev, + size_t nr, size_t iu_size) +{ + int err; + + spin_lock_init(&target->lock); + INIT_LIST_HEAD(&target->cmd_queue); + + target->dev = dev; + target->dev->driver_data = target; + + target->srp_iu_size = iu_size; + target->rx_ring_size = nr; + target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size); + if (!target->rx_ring) + return -ENOMEM; + err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring); + if (err) + goto free_ring; + + return 0; + +free_ring: + srp_ring_free(target->dev, target->rx_ring, nr, iu_size); + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(srp_target_alloc); + +void srp_target_free(struct srp_target *target) +{ + srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size, + target->srp_iu_size); + srp_iu_pool_free(&target->iu_queue); +} +EXPORT_SYMBOL_GPL(srp_target_free); + +struct iu_entry *srp_iu_get(struct srp_target *target) +{ + struct iu_entry *iue = NULL; + + kfifo_get(target->iu_queue.queue, (void *) &iue, sizeof(void *)); + BUG_ON(!iue); + + iue->target = target; + iue->scmd = NULL; + INIT_LIST_HEAD(&iue->ilist); + iue->flags = 0; + return iue; +} +EXPORT_SYMBOL_GPL(srp_iu_get); + +void srp_iu_put(struct iu_entry *iue) +{ + kfifo_put(iue->target->iu_queue.queue, (void *) &iue, sizeof(void *)); +} +EXPORT_SYMBOL_GPL(srp_iu_put); + +static int direct_data(struct scsi_cmnd *scmd, struct srp_direct_buf *md, + enum dma_data_direction dir, rdma_io_t rdma_io) +{ + struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr; + struct srp_target *target = iue->target; + struct scatterlist *sg = scmd->request_buffer; + int nsg, err; + + dprintk("%p %u %u %u %d\n", iue, scmd->request_bufflen, scmd->bufflen, + md->len, scmd->use_sg); + + nsg = dma_map_sg(target->dev, sg, scmd->use_sg, DMA_BIDIRECTIONAL); + if (!nsg) { + printk("fail to map %p %d\n", iue, scmd->use_sg); + return 0; + } + err = rdma_io(iue, sg, nsg, md, 1, dir, + min(scmd->request_bufflen, md->len)); + + dma_unmap_sg(target->dev, sg, nsg, DMA_BIDIRECTIONAL); + + return err; +} + +static int indirect_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd, + struct srp_indirect_buf *id, + enum dma_data_direction dir, rdma_io_t rdma_io) +{ + struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr; + struct srp_target *target = iue->target; + struct srp_direct_buf *md; + struct scatterlist dummy, *sg = scmd->request_buffer; + dma_addr_t token = 0; + long err; + unsigned int done = 0; + int nmd, nsg; + + nmd = id->table_desc.len / sizeof(struct srp_direct_buf); + + dprintk("%p %u %u %u %u %d %d %d\n", + iue, scmd->request_bufflen, scmd->bufflen, + id->len, scmd->offset, nmd, + cmd->data_in_desc_cnt, cmd->data_out_desc_cnt); + + if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) || + (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) { + md = &id->desc_list[0]; + goto rdma; + } + + md = dma_alloc_coherent(target->dev, id->table_desc.len, + &token, GFP_KERNEL); + if (!md) { + eprintk("Can't get dma memory %u\n", id->table_desc.len); + return 0; + } + + sg_init_one(&dummy, md, id->table_desc.len); + sg_dma_address(&dummy) = token; + err = rdma_io(iue, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE, + id->table_desc.len); + if (err < 0) { + eprintk("Error copying indirect table %ld\n", err); + goto free_mem; + } + +rdma: + nsg = dma_map_sg(target->dev, sg, scmd->use_sg, DMA_BIDIRECTIONAL); + if (!nsg) { + eprintk("fail to map %p %d\n", iue, scmd->use_sg); + goto free_mem; + } + + err = rdma_io(iue, sg, nsg, md, nmd, dir, + min(scmd->request_bufflen, id->len)); + dma_unmap_sg(target->dev, sg, nsg, DMA_BIDIRECTIONAL); + +free_mem: + if (token) + dma_free_coherent(target->dev, id->table_desc.len, md, token); + + return done; +} + +static int data_out_desc_size(struct srp_cmd *cmd) +{ + int size = 0; + u8 fmt = cmd->buf_fmt >> 4; + + switch (fmt) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + size = sizeof(struct srp_direct_buf); + break; + case SRP_DATA_DESC_INDIRECT: + size = sizeof(struct srp_indirect_buf) + + sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt; + break; + default: + eprintk("client error. Invalid data_out_format %x\n", fmt); + break; + } + return size; +} + +static int __srp_transfer_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd, + enum dma_data_direction dir, rdma_io_t rdma_io) +{ + struct srp_direct_buf *md; + struct srp_indirect_buf *id; + int offset, err = 0; + u8 format; + + offset = cmd->add_cdb_len * 4; + if (dir == DMA_FROM_DEVICE) + offset += data_out_desc_size(cmd); + + if (dir == DMA_TO_DEVICE) + format = cmd->buf_fmt >> 4; + else + format = cmd->buf_fmt & ((1U << 4) - 1); + + switch (format) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + md = (struct srp_direct_buf *) + (cmd->add_data + offset); + err = direct_data(scmd, md, dir, rdma_io); + break; + case SRP_DATA_DESC_INDIRECT: + id = (struct srp_indirect_buf *) + (cmd->add_data + offset); + err = indirect_data(scmd, cmd, id, dir, rdma_io); + break; + default: + eprintk("Unknown format %d %x\n", dir, format); + break; + } + + return err; +} + +/* TODO: this can be called multiple times for a single command. */ +int srp_transfer_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd, + rdma_io_t rdma_io) +{ + struct iu_entry *iue = (struct iu_entry *) scmd->SCp.ptr; + enum dma_data_direction dir; + + if (test_bit(V_WRITE, &iue->flags)) + dir = DMA_TO_DEVICE; + else + dir = DMA_FROM_DEVICE; + __srp_transfer_data(scmd, cmd, dir, rdma_io); + return 0; +} +EXPORT_SYMBOL_GPL(srp_transfer_data); + +static int vscsis_data_length(struct srp_cmd *cmd, enum dma_data_direction dir) +{ + struct srp_direct_buf *md; + struct srp_indirect_buf *id; + int len = 0, offset = cmd->add_cdb_len * 4; + u8 fmt; + + if (dir == DMA_TO_DEVICE) + fmt = cmd->buf_fmt >> 4; + else { + fmt = cmd->buf_fmt & ((1U << 4) - 1); + offset += data_out_desc_size(cmd); + } + + switch (fmt) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + md = (struct srp_direct_buf *) (cmd->add_data + offset); + len = md->len; + break; + case SRP_DATA_DESC_INDIRECT: + id = (struct srp_indirect_buf *) (cmd->add_data + offset); + len = id->len; + break; + default: + eprintk("invalid data format %x\n", fmt); + break; + } + return len; +} + +static uint8_t getcontrolbyte(u8 *cdb) +{ + return cdb[COMMAND_SIZE(cdb[0]) - 1]; +} + +static inline uint8_t getlink(struct srp_cmd *cmd) +{ + return (getcontrolbyte(cmd->cdb) & 0x01); +} + +int srp_cmd_perform(struct iu_entry *iue, struct srp_cmd *cmd) +{ + struct Scsi_Host *shost = iue->target->shost; + enum dma_data_direction data_dir; + struct scsi_cmnd *scmd; + int tag, len; + + if (getlink(cmd)) + __set_bit(V_LINKED, &iue->flags); + + tag = MSG_SIMPLE_TAG; + + switch (cmd->task_attr) { + case SRP_SIMPLE_TASK: + tag = MSG_SIMPLE_TAG; + break; + case SRP_ORDERED_TASK: + tag = MSG_ORDERED_TAG; + break; + case SRP_HEAD_TASK: + tag = MSG_HEAD_TAG; + break; + default: + eprintk("Task attribute %d not supported\n", cmd->task_attr); + tag = MSG_ORDERED_TAG; + } + + switch (cmd->cdb[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_VERIFY: + case WRITE_12: + case WRITE_VERIFY_12: + __set_bit(V_WRITE, &iue->flags); + } + + if (cmd->buf_fmt >> 4) + data_dir = DMA_TO_DEVICE; + else + data_dir = DMA_FROM_DEVICE; + len = vscsis_data_length(cmd, data_dir); + + dprintk("%p %x %lx %d %d %d %llx\n", iue, cmd->cdb[0], + cmd->lun, data_dir, len, tag, (unsigned long long) cmd->tag); + + scmd = scsi_host_get_command(shost, data_dir, GFP_KERNEL); + BUG_ON(!scmd); + scmd->SCp.ptr = (char *) iue; + memcpy(scmd->data_cmnd, cmd->cdb, MAX_COMMAND_SIZE); + scmd->request_bufflen = len; + scmd->tag = tag; + iue->scmd = scmd; + scsi_tgt_queue_command(scmd, (struct scsi_lun *) &cmd->lun, cmd->tag); + + return 0; +} +EXPORT_SYMBOL_GPL(srp_cmd_perform); + +MODULE_DESCRIPTION("SCSI RDAM Protocol lib functions"); +MODULE_AUTHOR("FUJITA Tomonori"); +MODULE_LICENSE("GPL"); diff -puN drivers/scsi/Makefile~git-scsi-target drivers/scsi/Makefile --- devel/drivers/scsi/Makefile~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/drivers/scsi/Makefile 2006-05-21 14:45:50.000000000 -0700 @@ -21,6 +21,7 @@ CFLAGS_seagate.o = -DARBITRATE -DPARIT subdir-$(CONFIG_PCMCIA) += pcmcia obj-$(CONFIG_SCSI) += scsi_mod.o +obj-$(CONFIG_SCSI_TGT) += scsi_tgt.o obj-$(CONFIG_RAID_ATTRS) += raid_class.o @@ -124,7 +125,9 @@ obj-$(CONFIG_SCSI_FCAL) += fcal.o obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o obj-$(CONFIG_SCSI_IPR) += ipr.o +obj-$(CONFIG_SCSI_SRP) += libsrp.o obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ +obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/ obj-$(CONFIG_SCSI_SATA_AHCI) += libata.o ahci.o obj-$(CONFIG_SCSI_PATA_AMD) += libata.o pata_amd.o obj-$(CONFIG_SCSI_SATA_SVW) += libata.o sata_svw.o @@ -168,6 +171,8 @@ scsi_mod-y += scsi.o hosts.o scsi_ioct scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o +scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o + sd_mod-objs := sd.o sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ diff -puN drivers/scsi/scsi.c~git-scsi-target drivers/scsi/scsi.c --- devel/drivers/scsi/scsi.c~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/drivers/scsi/scsi.c 2006-05-21 14:45:50.000000000 -0700 @@ -234,6 +234,58 @@ static struct scsi_cmnd *__scsi_get_comm } /* + * Function: scsi_host_get_command() + * + * Purpose: Allocate and setup a scsi command block and blk request + * + * Arguments: shost - scsi host + * data_dir - dma data dir + * gfp_mask- allocator flags + * + * Returns: The allocated scsi command structure. + * + * This should be called by target LLDs to get a command. + */ +struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost, + enum dma_data_direction data_dir, + gfp_t gfp_mask) +{ + int write = (data_dir == DMA_TO_DEVICE); + struct request *rq; + struct scsi_cmnd *cmd; + + /* Bail if we can't get a reference to the device */ + if (!get_device(&shost->shost_gendev)) + return NULL; + + rq = blk_get_request(shost->uspace_req_q, write, gfp_mask); + if (!rq) + goto put_dev; + + cmd = __scsi_get_command(shost, gfp_mask); + if (!cmd) + goto release_rq; + + memset(cmd, 0, sizeof(*cmd)); + cmd->sc_data_direction = data_dir; + cmd->jiffies_at_alloc = jiffies; + cmd->request = rq; + + rq->special = cmd; + rq->flags |= REQ_SPECIAL | REQ_BLOCK_PC; + + return cmd; + +release_rq: + blk_put_request(rq); +put_dev: + put_device(&shost->shost_gendev); + return NULL; + +} +EXPORT_SYMBOL_GPL(scsi_host_get_command); + +/* * Function: scsi_get_command() * * Purpose: Allocate and setup a scsi command block @@ -272,6 +324,43 @@ struct scsi_cmnd *scsi_get_command(struc EXPORT_SYMBOL(scsi_get_command); /* + * Function: scsi_host_put_command() + * + * Purpose: Free a scsi command block + * + * Arguments: shost - scsi host + * cmd - command block to free + * + * Returns: Nothing. + * + * Notes: The command must not belong to any lists. + */ +void scsi_host_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) +{ + struct request_queue *q = shost->uspace_req_q; + struct request *rq = cmd->request; + unsigned long flags; + + /* changing locks here, don't need to restore the irq state */ + spin_lock_irqsave(&shost->free_list_lock, flags); + if (unlikely(list_empty(&shost->free_list))) { + list_add(&cmd->list, &shost->free_list); + cmd = NULL; + } + spin_unlock(&shost->free_list_lock); + + spin_lock(q->queue_lock); + __blk_put_request(q, rq); + spin_unlock_irqrestore(q->queue_lock, flags); + + if (likely(cmd != NULL)) + kmem_cache_free(shost->cmd_pool->slab, cmd); + + put_device(&shost->shost_gendev); +} +EXPORT_SYMBOL_GPL(scsi_host_put_command); + +/* * Function: scsi_put_command() * * Purpose: Free a scsi command block diff -puN drivers/scsi/scsi_lib.c~git-scsi-target drivers/scsi/scsi_lib.c --- devel/drivers/scsi/scsi_lib.c~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/drivers/scsi/scsi_lib.c 2006-05-21 14:45:50.000000000 -0700 @@ -803,7 +803,7 @@ static struct scsi_cmnd *scsi_end_reques return NULL; } -static struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) +struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) { struct scsi_host_sg_pool *sgp; struct scatterlist *sgl; @@ -844,7 +844,9 @@ static struct scatterlist *scsi_alloc_sg return sgl; } -static void scsi_free_sgtable(struct scatterlist *sgl, int index) +EXPORT_SYMBOL(scsi_alloc_sgtable); + +void scsi_free_sgtable(struct scatterlist *sgl, int index) { struct scsi_host_sg_pool *sgp; @@ -854,6 +856,8 @@ static void scsi_free_sgtable(struct sca mempool_free(sgl, sgp->pool); } +EXPORT_SYMBOL(scsi_free_sgtable); + /* * Function: scsi_release_buffers() * @@ -1714,29 +1718,40 @@ u64 scsi_calculate_bounce_limit(struct S } EXPORT_SYMBOL(scsi_calculate_bounce_limit); -struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) +struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, + request_fn_proc *request_fn) { - struct Scsi_Host *shost = sdev->host; struct request_queue *q; - q = blk_init_queue(scsi_request_fn, NULL); + q = blk_init_queue(request_fn, NULL); if (!q) return NULL; - blk_queue_prep_rq(q, scsi_prep_fn); - blk_queue_max_hw_segments(q, shost->sg_tablesize); blk_queue_max_phys_segments(q, SCSI_MAX_PHYS_SEGMENTS); blk_queue_max_sectors(q, shost->max_sectors); blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost)); blk_queue_segment_boundary(q, shost->dma_boundary); - blk_queue_issue_flush_fn(q, scsi_issue_flush_fn); - blk_queue_softirq_done(q, scsi_softirq_done); if (!shost->use_clustering) clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags); return q; } +EXPORT_SYMBOL(__scsi_alloc_queue); + +struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) +{ + struct request_queue *q; + + q = __scsi_alloc_queue(sdev->host, scsi_request_fn); + if (!q) + return NULL; + + blk_queue_prep_rq(q, scsi_prep_fn); + blk_queue_issue_flush_fn(q, scsi_issue_flush_fn); + blk_queue_softirq_done(q, scsi_softirq_done); + return q; +} void scsi_free_queue(struct request_queue *q) { diff -puN /dev/null drivers/scsi/scsi_tgt_if.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/scsi/scsi_tgt_if.c 2006-05-21 14:45:50.000000000 -0700 @@ -0,0 +1,235 @@ +/* + * SCSI target kernel/user interface functions + * + * Copyright (C) 2005 FUJITA Tomonori + * Copyright (C) 2005 Mike Christie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi_tgt_priv.h" + +static int tgtd_pid; +static struct sock *nl_sk; + +static int send_event_rsp(uint16_t type, struct tgt_event *p, gfp_t flags, + pid_t pid) +{ + struct tgt_event *ev; + struct nlmsghdr *nlh; + struct sk_buff *skb; + uint32_t len; + + len = NLMSG_SPACE(sizeof(*ev)); + skb = alloc_skb(len, flags); + if (!skb) + return -ENOMEM; + + nlh = __nlmsg_put(skb, pid, 0, type, len - sizeof(*nlh), 0); + + ev = NLMSG_DATA(nlh); + memcpy(ev, p, sizeof(*ev)); + + return netlink_unicast(nl_sk, skb, pid, 0); +} + +int scsi_tgt_uspace_send(struct scsi_cmnd *cmd, struct scsi_lun *lun, u64 tag, + gfp_t flags) +{ + struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct tgt_event *ev; + int err, len; + + /* FIXME: we need scsi core to do that. */ + memcpy(cmd->cmnd, cmd->data_cmnd, MAX_COMMAND_SIZE); + + len = NLMSG_SPACE(sizeof(*ev)); + /* + * TODO: add MAX_COMMAND_SIZE to ev and add mempool + */ + skb = alloc_skb(NLMSG_SPACE(len), flags); + if (!skb) + return -ENOMEM; + + nlh = __nlmsg_put(skb, tgtd_pid, 0, TGT_KEVENT_CMD_REQ, + len - sizeof(*nlh), 0); + + ev = NLMSG_DATA(nlh); + ev->k.cmd_req.host_no = shost->host_no; + ev->k.cmd_req.cid = cmd->request->tag; + ev->k.cmd_req.data_len = cmd->request_bufflen; + memcpy(ev->k.cmd_req.scb, cmd->cmnd, sizeof(ev->k.cmd_req.scb)); + memcpy(ev->k.cmd_req.lun, lun, sizeof(ev->k.cmd_req.lun)); + ev->k.cmd_req.attribute = cmd->tag; + ev->k.cmd_req.tag = tag; + + dprintk("%p %d %u %u %x %llx\n", cmd, shost->host_no, ev->k.cmd_req.cid, + ev->k.cmd_req.data_len, cmd->tag, + (unsigned long long) ev->k.cmd_req.tag); + + err = netlink_unicast(nl_sk, skb, tgtd_pid, 0); + if (err < 0) + printk(KERN_ERR "scsi_tgt_uspace_send: could not send skb %d\n", + err); + return err; +} + +int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, gfp_t gfp_mask) +{ + struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); + struct tgt_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.k.cmd_done.host_no = shost->host_no; + ev.k.cmd_done.cid = cmd->request->tag; + ev.k.cmd_done.result = cmd->result; + + return send_event_rsp(TGT_KEVENT_CMD_DONE, &ev, gfp_mask, tgtd_pid); +} + +int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, + struct scsi_lun *scsilun, void *data) +{ + struct tgt_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.k.tsk_mgmt_req.host_no = host_no; + ev.k.tsk_mgmt_req.function = function; + ev.k.tsk_mgmt_req.tag = tag; + memcpy(ev.k.tsk_mgmt_req.lun, scsilun, sizeof(ev.k.tsk_mgmt_req.lun)); + ev.k.tsk_mgmt_req.mid = (u64) data; + + dprintk("%d %x %llx %llx\n", host_no, function, (unsigned long long) tag, + (unsigned long long) ev.k.tsk_mgmt_req.mid); + + return send_event_rsp(TGT_KEVENT_TSK_MGMT_REQ, &ev, GFP_KERNEL, tgtd_pid); +} + +static int event_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + struct tgt_event *ev = NLMSG_DATA(nlh); + int err = 0; + + dprintk("%d %d %d\n", nlh->nlmsg_type, + nlh->nlmsg_pid, current->pid); + + switch (nlh->nlmsg_type) { + case TGT_UEVENT_REQ: + tgtd_pid = NETLINK_CREDS(skb)->pid; + break; + case TGT_UEVENT_CMD_RSP: + /* TODO: handle multiple cmds in one event */ + err = scsi_tgt_kspace_exec(ev->u.cmd_rsp.host_no, + ev->u.cmd_rsp.cid, + ev->u.cmd_rsp.result, + ev->u.cmd_rsp.len, + ev->u.cmd_rsp.uaddr, + ev->u.cmd_rsp.rw); + break; + case TGT_UEVENT_TSK_MGMT_RSP: + err = scsi_tgt_kspace_tsk_mgmt(ev->u.tsk_mgmt_rsp.host_no, + ev->u.tsk_mgmt_rsp.mid, + ev->u.tsk_mgmt_rsp.result); + break; + default: + eprintk("unknown type %d\n", nlh->nlmsg_type); + err = -EINVAL; + } + + return err; +} + +static int event_recv_skb(struct sk_buff *skb) +{ + int err; + uint32_t rlen; + struct nlmsghdr *nlh; + struct tgt_event ev; + + while (skb->len >= NLMSG_SPACE(0)) { + nlh = (struct nlmsghdr *) skb->data; + if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) + return 0; + rlen = NLMSG_ALIGN(nlh->nlmsg_len); + if (rlen > skb->len) + rlen = skb->len; + err = event_recv_msg(skb, nlh); + + dprintk("%d %d\n", nlh->nlmsg_type, err); + /* + * TODO for passthru commands the lower level should + * probably handle the result or we should modify this + */ + switch (nlh->nlmsg_type) { + case TGT_UEVENT_CMD_RSP: + case TGT_UEVENT_TSK_MGMT_RSP: + break; + default: + memset(&ev, 0, sizeof(ev)); + ev.k.event_rsp.err = err; + send_event_rsp(TGT_KEVENT_RSP, &ev, + GFP_KERNEL | __GFP_NOFAIL, + nlh->nlmsg_pid); + } + skb_pull(skb, rlen); + } + return 0; +} + +static void event_recv(struct sock *sk, int length) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) { + if (NETLINK_CREDS(skb)->uid) { + skb_pull(skb, skb->len); + kfree_skb(skb); + continue; + } + + if (event_recv_skb(skb) && skb->len) + skb_queue_head(&sk->sk_receive_queue, skb); + else + kfree_skb(skb); + } +} + +void __exit scsi_tgt_if_exit(void) +{ + sock_release(nl_sk->sk_socket); +} + +int __init scsi_tgt_if_init(void) +{ + nl_sk = netlink_kernel_create(NETLINK_TGT, 1, event_recv, + THIS_MODULE); + if (!nl_sk) + return -ENOMEM; + + return 0; +} diff -puN /dev/null drivers/scsi/scsi_tgt_lib.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/scsi/scsi_tgt_lib.c 2006-05-21 14:45:50.000000000 -0700 @@ -0,0 +1,667 @@ +/* + * SCSI target lib functions + * + * Copyright (C) 2005 Mike Christie + * Copyright (C) 2005 FUJITA Tomonori + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include <../drivers/md/dm-bio-list.h> + +#include "scsi_tgt_priv.h" + +static struct workqueue_struct *scsi_tgtd; +static kmem_cache_t *scsi_tgt_cmd_cache; + +/* + * TODO: this struct will be killed when the block layer supports large bios + * and James's work struct code is in + */ +struct scsi_tgt_cmd { + /* TODO replace work with James b's code */ + struct work_struct work; + /* TODO replace the lists with a large bio */ + struct bio_list xfer_done_list; + struct bio_list xfer_list; + struct scsi_lun *lun; + + struct list_head hash_list; + struct request *rq; + u64 tag; +}; + +#define TGT_HASH_ORDER 4 +#define cmd_hashfn(cid) hash_long((cid), TGT_HASH_ORDER) + +struct scsi_tgt_queuedata { + struct Scsi_Host *shost; + struct list_head cmd_hash[1 << TGT_HASH_ORDER]; + spinlock_t cmd_hash_lock; + + struct work_struct uspace_send_work; + + spinlock_t cmd_req_lock; + struct mutex cmd_req_mutex; + struct list_head cmd_req; +}; + +static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd) +{ + struct bio *bio; + + /* must call bio_endio in case bio was bounced */ + while ((bio = bio_list_pop(&tcmd->xfer_done_list))) { + bio_endio(bio, bio->bi_size, 0); + bio_unmap_user(bio); + } + + while ((bio = bio_list_pop(&tcmd->xfer_list))) { + bio_endio(bio, bio->bi_size, 0); + bio_unmap_user(bio); + } +} + +static void scsi_tgt_cmd_destroy(void *data) +{ + struct scsi_cmnd *cmd = data; + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + struct scsi_tgt_queuedata *qdata = cmd->request->q->queuedata; + unsigned long flags; + + dprintk("cmd %p %d %lu\n", cmd, cmd->sc_data_direction, + rq_data_dir(cmd->request)); + + spin_lock_irqsave(&qdata->cmd_hash_lock, flags); + list_del(&tcmd->hash_list); + spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); + + /* + * We must set rq->flags here because bio_map_user and + * blk_rq_bio_prep ruined ti. + */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) + cmd->request->flags |= 1; + else + cmd->request->flags &= ~1UL; + + scsi_unmap_user_pages(tcmd); + kmem_cache_free(scsi_tgt_cmd_cache, tcmd); + scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd); +} + +static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd) +{ + struct scsi_tgt_queuedata *qdata = rq->q->queuedata; + unsigned long flags; + struct list_head *head; + static u32 tag = 0; + + spin_lock_irqsave(&qdata->cmd_hash_lock, flags); + rq->tag = tag++; + head = &qdata->cmd_hash[cmd_hashfn(rq->tag)]; + list_add(&tcmd->hash_list, head); + spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); +} + +static void scsi_tgt_uspace_send_fn(void *data) +{ + struct request_queue *q = data; + struct scsi_tgt_queuedata *qdata = q->queuedata; + struct request *rq; + struct scsi_cmnd *cmd; + struct scsi_tgt_cmd *tcmd; + unsigned long flags; + int err; + +retry: + err = 0; + if (list_empty(&qdata->cmd_req)) + return; + + mutex_lock(&qdata->cmd_req_mutex); + + spin_lock_irqsave(&qdata->cmd_req_lock, flags); + if (list_empty(&qdata->cmd_req)) { + spin_unlock_irqrestore(&qdata->cmd_req_lock, flags); + mutex_unlock(&qdata->cmd_req_mutex); + goto out; + } + rq = list_entry_rq(qdata->cmd_req.next); + list_del_init(&rq->queuelist); + spin_unlock_irqrestore(&qdata->cmd_req_lock, flags); + + tcmd = rq->end_io_data; + init_scsi_tgt_cmd(rq, tcmd); + cmd = rq->special; + err = scsi_tgt_uspace_send(cmd, tcmd->lun, tcmd->tag, GFP_ATOMIC); + if (err < 0) { + eprintk("failed to send: %p %d\n", cmd, err); + + spin_lock_irqsave(&qdata->cmd_req_lock, flags); + list_add(&rq->queuelist, &qdata->cmd_req); + spin_unlock_irqrestore(&qdata->cmd_req_lock, flags); + } + + mutex_unlock(&qdata->cmd_req_mutex); +out: + /* TODO: proper error handling */ + if (err < 0) + queue_delayed_work(scsi_tgtd, &qdata->uspace_send_work, + HZ / 10); + else + goto retry; +} + +/** + * scsi_tgt_alloc_queue - setup queue used for message passing + * shost: scsi host + * + * This should be called by the LLD after host allocation. + * And will be released when the host is released. + **/ +int scsi_tgt_alloc_queue(struct Scsi_Host *shost) +{ + struct scsi_tgt_queuedata *queuedata; + struct request_queue *q; + int err, i; + + /* + * Do we need to send a netlink event or should uspace + * just respond to the hotplug event? + */ + q = __scsi_alloc_queue(shost, NULL); + if (!q) + return -ENOMEM; + + queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL); + if (!queuedata) { + err = -ENOMEM; + goto cleanup_queue; + } + queuedata->shost = shost; + q->queuedata = queuedata; + + /* + * this is a silly hack. We should probably just queue as many + * command as is recvd to userspace. uspace can then make + * sure we do not overload the HBA + */ + q->nr_requests = shost->hostt->can_queue; + /* + * We currently only support software LLDs so this does + * not matter for now. Do we need this for the cards we support? + * If so we should make it a host template value. + */ + blk_queue_dma_alignment(q, 0); + shost->uspace_req_q = q; + + for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++) + INIT_LIST_HEAD(&queuedata->cmd_hash[i]); + spin_lock_init(&queuedata->cmd_hash_lock); + + INIT_LIST_HEAD(&queuedata->cmd_req); + spin_lock_init(&queuedata->cmd_req_lock); + INIT_WORK(&queuedata->uspace_send_work, scsi_tgt_uspace_send_fn, q); + mutex_init(&queuedata->cmd_req_mutex); + + return 0; + +cleanup_queue: + blk_cleanup_queue(q); + return err; +} +EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue); + +struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd) +{ + struct scsi_tgt_queuedata *queue = cmd->request->q->queuedata; + return queue->shost; +} +EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host); + +/** + * scsi_tgt_queue_command - queue command for userspace processing + * @cmd: scsi command + * @scsilun: scsi lun + * @noblock: set to nonzero if the command should be queued + **/ +int scsi_tgt_queue_command(struct scsi_cmnd *cmd, struct scsi_lun *scsilun, + u64 tag) +{ + struct request_queue *q = cmd->request->q; + struct scsi_tgt_queuedata *qdata = q->queuedata; + unsigned long flags; + struct scsi_tgt_cmd *tcmd; + + /* + * It would be better to allocate scsi_tgt_cmd structure in + * scsi_host_get_command and not to fail due to OOM. + */ + tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC); + if (!tcmd) + return -ENOMEM; + cmd->request->end_io_data = tcmd; + + bio_list_init(&tcmd->xfer_list); + bio_list_init(&tcmd->xfer_done_list); + tcmd->lun = scsilun; + tcmd->tag = tag; + tcmd->rq = cmd->request; + + spin_lock_irqsave(&qdata->cmd_req_lock, flags); + list_add_tail(&cmd->request->queuelist, &qdata->cmd_req); + spin_unlock_irqrestore(&qdata->cmd_req_lock, flags); + + queue_work(scsi_tgtd, &qdata->uspace_send_work); + return 0; +} +EXPORT_SYMBOL_GPL(scsi_tgt_queue_command); + +/* + * This is run from a interrpt handler normally and the unmap + * needs process context so we must queue + */ +static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd) +{ + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + + dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); + + scsi_tgt_uspace_send_status(cmd, GFP_ATOMIC); + INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy, cmd); + queue_work(scsi_tgtd, &tcmd->work); +} + +static int __scsi_tgt_transfer_response(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); + int err; + + dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); + + err = shost->hostt->transfer_response(cmd, scsi_tgt_cmd_done); + switch (err) { + case SCSI_MLQUEUE_HOST_BUSY: + case SCSI_MLQUEUE_DEVICE_BUSY: + return -EAGAIN; + } + + return 0; +} + +static void scsi_tgt_transfer_response(struct scsi_cmnd *cmd) +{ + int err; + + err = __scsi_tgt_transfer_response(cmd); + if (!err) + return; + + cmd->result = DID_BUS_BUSY << 16; + if (scsi_tgt_uspace_send_status(cmd, GFP_ATOMIC) <= 0) + /* the eh will have to pick this up */ + printk(KERN_ERR "Could not send cmd %p status\n", cmd); +} + +static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask) +{ + struct request *rq = cmd->request; + int count; + + cmd->use_sg = rq->nr_phys_segments; + cmd->request_buffer = scsi_alloc_sgtable(cmd, gfp_mask); + if (!cmd->request_buffer) + return -ENOMEM; + + cmd->request_bufflen = rq->data_len; + + dprintk("cmd %p addr %p cnt %d %lu\n", cmd, cmd->buffer, cmd->use_sg, + rq_data_dir(rq)); + count = blk_rq_map_sg(rq->q, rq, cmd->request_buffer); + if (likely(count <= cmd->use_sg)) { + cmd->use_sg = count; + return 0; + } + + eprintk("cmd %p addr %p cnt %d\n", cmd, cmd->buffer, cmd->use_sg); + scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); + return -EINVAL; +} + +/* TODO: test this crap and replace bio_map_user with new interface maybe */ +static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, + int rw) +{ + struct request_queue *q = cmd->request->q; + struct request *rq = cmd->request; + void *uaddr = cmd->buffer; + unsigned int len = cmd->bufflen; + struct bio *bio; + int err; + + while (len > 0) { + dprintk("%lx %u\n", (unsigned long) uaddr, len); + bio = bio_map_user(q, NULL, (unsigned long) uaddr, len, rw); + if (IS_ERR(bio)) { + err = PTR_ERR(bio); + dprintk("fail to map %lx %u %d %x\n", + (unsigned long) uaddr, len, err, cmd->cmnd[0]); + goto unmap_bios; + } + + uaddr += bio->bi_size; + len -= bio->bi_size; + + /* + * The first bio is added and merged. We could probably + * try to add others using scsi_merge_bio() but for now + * we keep it simple. The first bio should be pretty large + * (either hitting the 1 MB bio pages limit or a queue limit) + * already but for really large IO we may want to try and + * merge these. + */ + if (!rq->bio) { + blk_rq_bio_prep(q, rq, bio); + rq->data_len = bio->bi_size; + } else + /* put list of bios to transfer in next go around */ + bio_list_add(&tcmd->xfer_list, bio); + } + + cmd->offset = 0; + err = scsi_tgt_init_cmd(cmd, GFP_KERNEL); + if (err) + goto unmap_bios; + + return 0; + +unmap_bios: + if (rq->bio) { + bio_unmap_user(rq->bio); + while ((bio = bio_list_pop(&tcmd->xfer_list))) + bio_unmap_user(bio); + } + + return err; +} + +static int scsi_tgt_transfer_data(struct scsi_cmnd *); + +static void scsi_tgt_data_transfer_done(struct scsi_cmnd *cmd) +{ + struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; + struct bio *bio; + int err; + + /* should we free resources here on error ? */ + if (cmd->result) { +send_uspace_err: + if (scsi_tgt_uspace_send_status(cmd, GFP_ATOMIC) <= 0) + /* the tgt uspace eh will have to pick this up */ + printk(KERN_ERR "Could not send cmd %p status\n", cmd); + return; + } + + dprintk("cmd %p request_bufflen %u bufflen %u\n", + cmd, cmd->request_bufflen, cmd->bufflen); + + scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); + bio_list_add(&tcmd->xfer_done_list, cmd->request->bio); + + cmd->buffer += cmd->request_bufflen; + cmd->offset += cmd->request_bufflen; + + if (!tcmd->xfer_list.head) { + scsi_tgt_transfer_response(cmd); + return; + } + + dprintk("cmd2 %p request_bufflen %u bufflen %u\n", + cmd, cmd->request_bufflen, cmd->bufflen); + + bio = bio_list_pop(&tcmd->xfer_list); + BUG_ON(!bio); + + blk_rq_bio_prep(cmd->request->q, cmd->request, bio); + cmd->request->data_len = bio->bi_size; + err = scsi_tgt_init_cmd(cmd, GFP_ATOMIC); + if (err) { + cmd->result = DID_ERROR << 16; + goto send_uspace_err; + } + + if (scsi_tgt_transfer_data(cmd)) { + cmd->result = DID_NO_CONNECT << 16; + goto send_uspace_err; + } +} + +static int scsi_tgt_transfer_data(struct scsi_cmnd *cmd) +{ + int err; + struct Scsi_Host *host = scsi_tgt_cmd_to_host(cmd); + + err = host->hostt->transfer_data(cmd, scsi_tgt_data_transfer_done); + switch (err) { + case SCSI_MLQUEUE_HOST_BUSY: + case SCSI_MLQUEUE_DEVICE_BUSY: + return -EAGAIN; + default: + return 0; + } +} + +static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr, + unsigned len) +{ + char __user *p = (char __user *) uaddr; + + if (copy_from_user(cmd->sense_buffer, p, + min_t(unsigned, SCSI_SENSE_BUFFERSIZE, len))) { + printk(KERN_ERR "Could not copy the sense buffer\n"); + return -EIO; + } + return 0; +} + +static int scsi_tgt_abort_cmd(struct Scsi_Host *host, struct scsi_cmnd *cmd) +{ + int err; + + err = host->hostt->eh_abort_handler(cmd); + if (err) + eprintk("fail to abort %p\n", cmd); + + scsi_tgt_cmd_destroy(cmd); + return err; +} + +static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u32 cid) +{ + struct scsi_tgt_queuedata *qdata = q->queuedata; + struct request *rq = NULL; + struct list_head *head; + struct scsi_tgt_cmd *tcmd; + unsigned long flags; + + head = &qdata->cmd_hash[cmd_hashfn(cid)]; + spin_lock_irqsave(&qdata->cmd_hash_lock, flags); + list_for_each_entry(tcmd, head, hash_list) { + if (tcmd->rq->tag == cid) { + rq = tcmd->rq; + break; + } + } + spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); + + return rq; +} + +int scsi_tgt_kspace_exec(int host_no, u32 cid, int result, u32 len, + unsigned long uaddr, u8 rw) +{ + struct Scsi_Host *shost; + struct scsi_cmnd *cmd; + struct request *rq; + int err = 0; + + dprintk("%d %u %d %u %lx %u\n", host_no, cid, result, + len, uaddr, rw); + + /* TODO: replace with a O(1) alg */ + shost = scsi_host_lookup(host_no); + if (IS_ERR(shost)) { + printk(KERN_ERR "Could not find host no %d\n", host_no); + return -EINVAL; + } + + rq = tgt_cmd_hash_lookup(shost->uspace_req_q, cid); + if (!rq) { + printk(KERN_ERR "Could not find cid %u\n", cid); + err = -EINVAL; + goto done; + } + cmd = rq->special; + + dprintk("cmd %p result %d len %d bufflen %u %lu %x\n", cmd, + result, len, cmd->request_bufflen, rq_data_dir(rq), cmd->cmnd[0]); + + if (result == TASK_ABORTED) { + scsi_tgt_abort_cmd(shost, cmd); + goto done; + } + /* + * store the userspace values here, the working values are + * in the request_* values + */ + cmd->buffer = (void *)uaddr; + if (len) + cmd->bufflen = len; + cmd->result = result; + + if (!cmd->bufflen) { + err = __scsi_tgt_transfer_response(cmd); + goto done; + } + + /* + * TODO: Do we need to handle case where request does not + * align with LLD. + */ + err = scsi_map_user_pages(rq->end_io_data, cmd, rw); + if (err) { + eprintk("%p %d\n", cmd, err); + err = -EAGAIN; + goto done; + } + + /* userspace failure */ + if (cmd->result) { + if (status_byte(cmd->result) == CHECK_CONDITION) + scsi_tgt_copy_sense(cmd, uaddr, len); + err = __scsi_tgt_transfer_response(cmd); + goto done; + } + /* ask the target LLD to transfer the data to the buffer */ + err = scsi_tgt_transfer_data(cmd); + +done: + scsi_host_put(shost); + return err; +} + +int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, int function, u64 tag, + struct scsi_lun *scsilun, void *data) +{ + int err; + + /* TODO: need to retry if this fails. */ + err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, function, + tag, scsilun, data); + if (err < 0) + eprintk("The task management request lost!\n"); + return err; +} +EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request); + +int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result) +{ + struct Scsi_Host *shost; + int err; + + dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid); + + shost = scsi_host_lookup(host_no); + if (IS_ERR(shost)) { + printk(KERN_ERR "Could not find host no %d\n", host_no); + return -EINVAL; + } + err = shost->hostt->tsk_mgmt_response(mid, result); + scsi_host_put(shost); + + return err; +} + +static int __init scsi_tgt_init(void) +{ + int err; + + scsi_tgt_cmd_cache = kmem_cache_create("scsi_tgt_cmd", + sizeof(struct scsi_tgt_cmd), + 0, 0, NULL, NULL); + if (!scsi_tgt_cmd_cache) + return -ENOMEM; + + scsi_tgtd = create_workqueue("scsi_tgtd"); + if (!scsi_tgtd) { + err = -ENOMEM; + goto free_kmemcache; + } + + err = scsi_tgt_if_init(); + if (err) + goto destroy_wq; + + return 0; + +destroy_wq: + destroy_workqueue(scsi_tgtd); +free_kmemcache: + kmem_cache_destroy(scsi_tgt_cmd_cache); + return err; +} + +static void __exit scsi_tgt_exit(void) +{ + destroy_workqueue(scsi_tgtd); + scsi_tgt_if_exit(); + kmem_cache_destroy(scsi_tgt_cmd_cache); +} + +module_init(scsi_tgt_init); +module_exit(scsi_tgt_exit); + +MODULE_DESCRIPTION("SCSI target core"); +MODULE_LICENSE("GPL"); diff -puN /dev/null drivers/scsi/scsi_tgt_priv.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/scsi/scsi_tgt_priv.h 2006-05-21 14:45:50.000000000 -0700 @@ -0,0 +1,24 @@ +struct scsi_cmnd; +struct scsi_lun; +struct Scsi_Host; +struct task_struct; + +/* tmp - will replace with SCSI logging stuff */ +#define eprintk(fmt, args...) \ +do { \ + printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ +} while (0) + +#define dprintk eprintk + +extern void scsi_tgt_if_exit(void); +extern int scsi_tgt_if_init(void); + +extern int scsi_tgt_uspace_send(struct scsi_cmnd *cmd, struct scsi_lun *lun, + u64 tag, gfp_t flags); +extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, gfp_t flags); +extern int scsi_tgt_kspace_exec(int host_no, u32 cid, int result, u32 len, + unsigned long uaddr, u8 rw); +extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, + struct scsi_lun *scsilun, void *data); +extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result); diff -puN fs/bio.c~git-scsi-target fs/bio.c --- devel/fs/bio.c~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/fs/bio.c 2006-05-21 14:45:50.000000000 -0700 @@ -621,10 +621,9 @@ static struct bio *__bio_map_user_iov(re nr_pages += end - start; /* - * transfer and buffer must be aligned to at least hardsector - * size for now, in the future we can relax this restriction + * buffer must be aligned to at least hardsector size for now */ - if ((uaddr & queue_dma_alignment(q)) || (len & queue_dma_alignment(q))) + if (uaddr & queue_dma_alignment(q)) return ERR_PTR(-EINVAL); } @@ -749,7 +748,6 @@ struct bio *bio_map_user_iov(request_que int write_to_vm) { struct bio *bio; - int len = 0, i; bio = __bio_map_user_iov(q, bdev, iov, iov_count, write_to_vm); @@ -764,18 +762,7 @@ struct bio *bio_map_user_iov(request_que */ bio_get(bio); - for (i = 0; i < iov_count; i++) - len += iov[i].iov_len; - - if (bio->bi_size == len) - return bio; - - /* - * don't support partial mappings - */ - bio_endio(bio, bio->bi_size, 0); - bio_unmap_user(bio); - return ERR_PTR(-EINVAL); + return bio; } static void __bio_unmap_user(struct bio *bio) diff -puN include/linux/blkdev.h~git-scsi-target include/linux/blkdev.h --- devel/include/linux/blkdev.h~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/include/linux/blkdev.h 2006-05-21 14:45:50.000000000 -0700 @@ -623,7 +623,8 @@ extern void blk_queue_activity_fn(reques extern int blk_rq_map_user(request_queue_t *, struct request *, void __user *, unsigned int); extern int blk_rq_unmap_user(struct bio *, unsigned int); extern int blk_rq_map_kern(request_queue_t *, struct request *, void *, unsigned int, gfp_t); -extern int blk_rq_map_user_iov(request_queue_t *, struct request *, struct sg_iovec *, int); +extern int blk_rq_map_user_iov(request_queue_t *, struct request *, + struct sg_iovec *, int, unsigned int); extern int blk_execute_rq(request_queue_t *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(request_queue_t *, struct gendisk *, diff -puN include/linux/netlink.h~git-scsi-target include/linux/netlink.h --- devel/include/linux/netlink.h~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/include/linux/netlink.h 2006-05-21 14:45:50.000000000 -0700 @@ -21,6 +21,7 @@ #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ #define NETLINK_GENERIC 16 +#define NETLINK_TGT 17 /* SCSI target */ #define MAX_LINKS 32 diff -puN /dev/null include/scsi/libsrp.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/include/scsi/libsrp.h 2006-05-21 14:45:50.000000000 -0700 @@ -0,0 +1,75 @@ +#ifndef __LIBSRP_H__ +#define __LIBSRP_H__ + +#include +#include +#include +#include + +enum iue_flags { + V_DIOVER, + V_WRITE, + V_LINKED, + V_FLYING, +}; + +struct srp_buf { + dma_addr_t dma; + void *buf; +}; + +struct srp_queue { + void *pool; + void *items; + struct kfifo *queue; + spinlock_t lock; +}; + +struct srp_target { + struct Scsi_Host *shost; + struct device *dev; + + spinlock_t lock; + struct list_head cmd_queue; + + size_t srp_iu_size; + struct srp_queue iu_queue; + size_t rx_ring_size; + struct srp_buf **rx_ring; + + /* IB needs tx_ring too */ + + void *ldata; +}; + +struct iu_entry { + struct srp_target *target; + struct scsi_cmnd *scmd; + + struct list_head ilist; + dma_addr_t remote_token; + unsigned long flags; + + struct srp_buf *sbuf; +}; + +typedef int (rdma_io_t) (struct iu_entry *, struct scatterlist *, int, + struct srp_direct_buf *, int, + enum dma_data_direction, unsigned int); + +static inline struct srp_target *host_to_target(struct Scsi_Host *host) +{ + return (struct srp_target *) host->hostdata; +} + +extern int srp_target_alloc(struct srp_target *, struct device *, size_t, size_t); +extern void srp_target_free(struct srp_target *); + +extern struct iu_entry *srp_iu_get(struct srp_target *); +extern void srp_iu_put(struct iu_entry *); + +extern int srp_cmd_perform(struct iu_entry *iue, struct srp_cmd *cmd); +extern int srp_transfer_data(struct scsi_cmnd *scmd, struct srp_cmd *cmd, + rdma_io_t rdma_io); + +#endif diff -puN include/scsi/scsi_cmnd.h~git-scsi-target include/scsi/scsi_cmnd.h --- devel/include/scsi/scsi_cmnd.h~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/include/scsi/scsi_cmnd.h 2006-05-21 14:45:50.000000000 -0700 @@ -8,6 +8,7 @@ struct request; struct scatterlist; +struct Scsi_Host; struct scsi_device; struct scsi_request; @@ -84,6 +85,8 @@ struct scsi_cmnd { unsigned short sglist_len; /* size of malloc'd scatter-gather list */ unsigned bufflen; /* Size of data buffer */ void *buffer; /* Data buffer */ + /* offset in cmd we are at (for multi-transfer tgt cmds) */ + unsigned offset; unsigned underflow; /* Return error if less than this amount is transferred */ diff -puN include/scsi/scsi_host.h~git-scsi-target include/scsi/scsi_host.h --- devel/include/scsi/scsi_host.h~git-scsi-target 2006-05-21 14:45:50.000000000 -0700 +++ devel-akpm/include/scsi/scsi_host.h 2006-05-21 14:45:50.000000000 -0700 @@ -7,6 +7,7 @@ #include #include +struct request_queue; struct block_device; struct completion; struct module; @@ -123,6 +124,39 @@ struct scsi_host_template { void (*done)(struct scsi_cmnd *)); /* + * The transfer functions are used to queue a scsi command to + * the LLD. When the driver is finished processing the command + * the done callback is invoked. + * + * return values: see queuecommand + * + * If the LLD accepts the cmd, it should set the result to an + * appropriate value when completed before calling the done function. + * + * STATUS: REQUIRED FOR TARGET DRIVERS + */ + /* TODO: rename */ + int (* transfer_response)(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); + /* + * This is called to inform the LLD to transfer cmd->request_bufflen + * bytes of the cmd at cmd->offset in the cmd. The cmd->use_sg + * speciefies the number of scatterlist entried in the command + * and cmd->request_buffer contains the scatterlist. + * + * If the command cannot be processed in one transfer_data call + * becuase a scatterlist within the LLD's limits cannot be + * created then transfer_data will be called multiple times. + * It is initially called from process context, and later + * calls are from the interrup context. + */ + int (* transfer_data)(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); + + /* Used as callback for the completion of task management request. */ + int (* tsk_mgmt_response)(u64 mid, int result); + + /* * This is an error handling strategy routine. You don't need to * define one of these if you don't want to - there is a default * routine that is present that should work in most cases. For those @@ -558,6 +592,12 @@ struct Scsi_Host { */ unsigned int max_host_blocked; + /* + * q used for scsi_tgt msgs, async events or any other requests that + * need to be processed in userspace + */ + struct request_queue *uspace_req_q; + /* legacy crap */ unsigned long base; unsigned long io_port; @@ -660,6 +700,9 @@ extern void scsi_unblock_requests(struct extern void scsi_block_requests(struct Scsi_Host *); struct class_container; + +extern struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, + void (*) (struct request_queue *)); /* * These two functions are used to allocate and free a pseudo device * which will connect to the host adapter itself rather than any diff -puN /dev/null include/scsi/scsi_tgt.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/include/scsi/scsi_tgt.h 2006-05-21 14:45:50.000000000 -0700 @@ -0,0 +1,13 @@ +/* + * SCSI target definitions + */ + +struct Scsi_Host; +struct scsi_cmnd; +struct scsi_lun; + +extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *); +extern int scsi_tgt_alloc_queue(struct Scsi_Host *); +extern int scsi_tgt_queue_command(struct scsi_cmnd *, struct scsi_lun *, u64); +extern int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *, int, u64, struct scsi_lun *, + void *); diff -puN /dev/null include/scsi/scsi_tgt_if.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/include/scsi/scsi_tgt_if.h 2006-05-21 14:45:50.000000000 -0700 @@ -0,0 +1,89 @@ +/* + * SCSI target kernel/user interface + * + * Copyright (C) 2005 FUJITA Tomonori + * Copyright (C) 2005 Mike Christie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef __SCSI_TARGET_IF_H +#define __SCSI_TARGET_IF_H + +enum tgt_event_type { + /* user -> kernel */ + TGT_UEVENT_REQ, + TGT_UEVENT_CMD_RSP, + TGT_UEVENT_TSK_MGMT_RSP, + + /* kernel -> user */ + TGT_KEVENT_RSP, + TGT_KEVENT_CMD_REQ, + TGT_KEVENT_CMD_DONE, + TGT_KEVENT_TSK_MGMT_REQ, +}; + +struct tgt_event { + /* user-> kernel */ + union { + struct { + int type; + int host_no; + } event_req; + struct { + int host_no; + uint32_t cid; + uint32_t len; + int result; + uint64_t uaddr; + uint8_t rw; + } cmd_rsp; + struct { + int host_no; + uint64_t mid; + int result; + } tsk_mgmt_rsp; + } u; + + /* kernel -> user */ + union { + struct { + int err; + } event_rsp; + struct { + int host_no; + uint32_t cid; + uint32_t data_len; + uint8_t scb[16]; + uint8_t lun[8]; + int attribute; + uint64_t tag; + } cmd_req; + struct { + int host_no; + uint32_t cid; + int result; + } cmd_done; + struct { + int host_no; + int function; + uint64_t tag; + uint8_t lun[8]; + uint64_t mid; + } tsk_mgmt_req; + } k; + +} __attribute__ ((aligned (sizeof(uint64_t)))); +#endif _