[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 1/4] add scsi-target and IO_CMD_EPOLL_WAIT patches
This includes two kernel patches, scsi-target and IO_CMD_EPOLL_WAIT. The former is a modified version of the scsi target infrastructure in mainline. The latter enables applications to handle AIO and non-AIO fds in the same loop. blktap uses the different patch, AIO event queue patch for the same aim. The IO_CMD_EPOLL_WAIT patch will be merged into mainline (and the AIO event queue will not) so the scsi target daemon uses it. Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> diff -r 489f28021f26 -r 105d5d6b4e0d patches/linux-2.6.16.33/series --- a/patches/linux-2.6.16.33/series Wed Dec 20 09:47:24 2006 +0000 +++ b/patches/linux-2.6.16.33/series Wed Jan 03 01:34:02 2007 +0900 @@ -33,3 +33,5 @@ x86-elfnote-as-preprocessor-macro.patch x86-elfnote-as-preprocessor-macro.patch vsnprintf.patch kasprintf.patch +epoll-aio.patch +scsi-target.patch diff -r 489f28021f26 -r 105d5d6b4e0d patches/linux-2.6.16.33/epoll-aio.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.33/epoll-aio.patch Wed Jan 03 01:34:02 2007 +0900 @@ -0,0 +1,241 @@ +diff --git a/fs/aio.c b/fs/aio.c +index aec2b19..a1a4c2c 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -29,6 +29,7 @@ #include <linux/aio.h> + #include <linux/highmem.h> + #include <linux/workqueue.h> + #include <linux/security.h> ++#include <linux/eventpoll.h> + + #include <asm/kmap_types.h> + #include <asm/uaccess.h> +@@ -812,6 +813,7 @@ static void aio_queue_work(struct kioctx + timeout = 1; + else + timeout = HZ/10; ++ timeout = 1; + queue_delayed_work(aio_wq, &ctx->wq, timeout); + } + +@@ -1435,6 +1437,9 @@ static ssize_t aio_setup_iocb(struct kio + if (file->f_op->aio_fsync) + kiocb->ki_retry = aio_fsync; + break; ++ case IOCB_CMD_EPOLL_WAIT: ++ kiocb->ki_retry = eventpoll_aio_wait; ++ break; + default: + dprintk("EINVAL: io_submit: no operation provided\n"); + ret = -EINVAL; +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 4284cd3..3aca096 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -34,6 +34,7 @@ #include <linux/wait.h> + #include <linux/eventpoll.h> + #include <linux/mount.h> + #include <linux/bitops.h> ++#include <linux/aio.h> + #include <asm/uaccess.h> + #include <asm/system.h> + #include <asm/io.h> +@@ -706,6 +707,150 @@ eexit_1: + return error; + } + ++static void eventpoll_aio_timer(unsigned long data) ++{ ++ struct kiocb *iocb = (struct kiocb *)data; ++ struct timer_list *timer = (struct timer_list *)iocb->private; ++ struct file *file = iocb->ki_filp; ++ struct eventpoll *ep = (struct eventpoll *)file->private_data; ++ unsigned long flags; ++ ++ (void)del_timer(timer); ++ write_lock_irqsave(&ep->lock, flags); ++ __wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE); ++ write_unlock_irqrestore(&ep->lock, flags); ++} ++ ++static int aio_epoll_cancel(struct kiocb *iocb, struct io_event *event) ++{ ++ struct file *file = iocb->ki_filp; ++ struct eventpoll *ep = (struct eventpoll *)file->private_data; ++ int ret = -1; ++ struct list_head *list; ++ int seen = 0; ++ ++ write_lock_irq(&ep->lock); ++ ++ if (iocb->private) ++ del_timer((struct timer_list *)iocb->private); ++ /* ++ * We need to know whether the event was removed from the wait ++ * queue in order to return the proper status to the cancellation ++ * code. ++ */ ++ list = &ep->wq.task_list; ++ ++ do { ++ struct list_head *next; ++ if (list == &iocb->ki_wait.task_list) ++ seen++; ++ next = list->next; ++ if (next->prev != list) { ++ seen += 2; ++ break; ++ } ++ list = next; ++ } while (list != &ep->wq.task_list); ++ ++ if (seen == 1) { ++ __remove_wait_queue(&ep->wq, &iocb->ki_wait); ++ ret = 0; ++ } ++ write_unlock_irq(&ep->lock); ++ ++ if (ret == 0) { ++ /* successfully cancelled request */ ++ kfree(iocb->private); ++ iocb->private = NULL; ++ /* drop the i/o reference */ ++ aio_put_req(iocb); ++ } else ++ ret = -EAGAIN; ++ ++ event->res = event->res2 = 0; ++ /* drop the cancel reference */ ++ aio_put_req(iocb); ++ ++ return ret; ++} ++ ++/* ++ * iocb->ki_nbytes -- number of events ++ * iocb->ki_pos -- relative timeout in milliseconds ++ * iocb->private -- NULL first go; after that, it's set to the the ++ * absolute timeout in jiffies. ++ */ ++ssize_t eventpoll_aio_wait(struct kiocb *iocb) ++{ ++ struct file *file = iocb->ki_filp; ++ ssize_t ret = -EINVAL; ++ int relative_ms; ++ unsigned long expires; ++ unsigned long now; ++ struct timer_list *timer; ++ ++ if (!is_file_epoll(file) || iocb->ki_nbytes > MAX_EVENTS || ++ iocb->ki_nbytes <= 0) ++ return -EINVAL; ++ ++ if (!iocb->private) { ++ /* ++ * Note that we unconditionally allocate a timer, but we ++ * only use it if a timeout was specified. Otherwise, it ++ * is just a holder for the "infinite" value. ++ */ ++ timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL); ++ if (!timer) ++ return -ENOMEM; ++ ++ if ((long)iocb->ki_pos < 0 || iocb->ki_pos >= EP_MAX_MSTIMEO) ++ expires = MAX_SCHEDULE_TIMEOUT; ++ else ++ expires = jiffies + msecs_to_jiffies(iocb->ki_pos); ++ ++ init_timer(timer); ++ timer->function = eventpoll_aio_timer; ++ timer->data = (unsigned long)iocb; ++ timer->expires = expires; ++ } else { ++ timer = (struct timer_list *)iocb->private; ++ expires = timer->expires; ++ } ++ ++ now = jiffies; ++ if (timer->expires == MAX_SCHEDULE_TIMEOUT) ++ relative_ms = EP_MAX_MSTIMEO; ++ else if (time_before(now, expires)) ++ relative_ms = jiffies_to_msecs(expires - now); ++ else ++ relative_ms = 0; ++ ++ iocb->ki_cancel = aio_epoll_cancel; ++ ret = ep_poll(file->private_data, ++ (struct epoll_event __user *)iocb->ki_buf, ++ iocb->ki_nbytes, relative_ms); ++ ++ /* ++ * If a timeout was specified, ep_poll returned retry, and we have ++ * not yet registered a timer, go ahead and register one. ++ */ ++ if (ret == -EIOCBRETRY && !iocb->private) { ++ iocb->private = timer; ++ add_timer(timer); ++ } ++ ++ /* ++ * Did we get any events? ++ */ ++ if (ret >= 0) { ++ iocb->ki_cancel = NULL; ++ (void)del_timer(timer); ++ kfree(timer); ++ iocb->private = NULL; ++ } ++ ++ return ret; ++} + + /* + * Creates the file descriptor to be used by the epoll interface. +@@ -1518,6 +1663,12 @@ retry: + + res = 0; + if (list_empty(&ep->rdllist)) { ++ if (in_aio() && jtimeout) { ++ __add_wait_queue(&ep->wq, current->io_wait); ++ res = -EIOCBRETRY; ++ write_unlock_irqrestore(&ep->lock, flags); ++ goto out; ++ } + /* + * We don't have any available event to return to the caller. + * We need to sleep here, and we will be wake up by +@@ -1562,7 +1713,7 @@ retry: + if (!res && eavail && + !(res = ep_events_transfer(ep, events, maxevents)) && jtimeout) + goto retry; +- ++out: + return res; + } + +diff --git a/include/linux/aio_abi.h b/include/linux/aio_abi.h +index 30fdcc8..bb67d5b 100644 +--- a/include/linux/aio_abi.h ++++ b/include/linux/aio_abi.h +@@ -41,6 +41,7 @@ enum { + * IOCB_CMD_POLL = 5, + */ + IOCB_CMD_NOOP = 6, ++ IOCB_CMD_EPOLL_WAIT = 9, + }; + + /* read() from /dev/aio returns these structures. */ +diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h +index 1289f0e..8b6678e 100644 +--- a/include/linux/eventpoll.h ++++ b/include/linux/eventpoll.h +@@ -57,6 +57,9 @@ void eventpoll_init_file(struct file *fi + /* Used to release the epoll bits inside the "struct file" */ + void eventpoll_release_file(struct file *file); + ++/* Used to provide epoll_wait() to sys_io_submit() */ ++ssize_t eventpoll_aio_wait(struct kiocb *iocb); ++ + /* + * This is called from inside fs/file_table.c:__fput() to unlink files + * from the eventpoll interface. We need to have this facility to cleanup diff -r 489f28021f26 -r 105d5d6b4e0d patches/linux-2.6.16.33/scsi-target.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.33/scsi-target.patch Wed Jan 03 01:34:02 2007 +0900 @@ -0,0 +1,3571 @@ +diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c +index 1ce88cf..c631d5a 100644 +--- a/block/ll_rw_blk.c ++++ b/block/ll_rw_blk.c +@@ -2265,6 +2265,84 @@ void blk_insert_request(request_queue_t + + EXPORT_SYMBOL(blk_insert_request); + ++static int __blk_rq_unmap_user(struct bio *bio) ++{ ++ int ret = 0; ++ ++ if (bio) { ++ if (bio_flagged(bio, BIO_USER_MAPPED)) ++ bio_unmap_user(bio); ++ else ++ ret = bio_uncopy_user(bio); ++ } ++ ++ return ret; ++} ++ ++static int __blk_rq_map_user(request_queue_t *q, struct request *rq, ++ void __user *ubuf, unsigned int len) ++{ ++ unsigned long uaddr; ++ struct bio *bio, *orig_bio; ++ int reading, ret; ++ ++ reading = rq_data_dir(rq) == READ; ++ ++ /* ++ * if alignment requirement is satisfied, map in user pages for ++ * direct dma. else, set up kernel bounce buffers ++ */ ++ uaddr = (unsigned long) ubuf; ++ if (!(uaddr & queue_dma_alignment(q)) && !(len & queue_dma_alignment(q))) ++ bio = bio_map_user(q, NULL, uaddr, len, reading); ++ else ++ bio = bio_copy_user(q, uaddr, len, reading); ++ ++ if (IS_ERR(bio)) { ++ return PTR_ERR(bio); ++ } ++ ++ orig_bio = bio; ++ blk_queue_bounce(q, &bio); ++ /* ++ * We link the bounce buffer in and could have to traverse it ++ * later so we have to get a ref to prevent it from being freed ++ */ ++ bio_get(bio); ++ ++ /* ++ * for most (all? don't know of any) queues we could ++ * skip grabbing the queue lock here. only drivers with ++ * funky private ->back_merge_fn() function could be ++ * problematic. ++ */ ++ spin_lock_irq(q->queue_lock); ++ if (!rq->bio) ++ blk_rq_bio_prep(q, rq, bio); ++ else if (!q->back_merge_fn(q, rq, bio)) { ++ ret = -EINVAL; ++ spin_unlock_irq(q->queue_lock); ++ goto unmap_bio; ++ } else { ++ rq->biotail->bi_next = bio; ++ rq->biotail = bio; ++ ++ rq->nr_sectors += bio_sectors(bio); ++ rq->hard_nr_sectors = rq->nr_sectors; ++ rq->data_len += bio->bi_size; ++ } ++ spin_unlock_irq(q->queue_lock); ++ ++ return bio->bi_size; ++ ++unmap_bio: ++ /* if it was boucned we must call the end io function */ ++ bio_endio(bio, bio->bi_size, 0); ++ __blk_rq_unmap_user(orig_bio); ++ bio_put(bio); ++ return ret; ++} ++ + /** + * blk_rq_map_user - map user data to a request, for REQ_BLOCK_PC usage + * @q: request queue where request should be inserted +@@ -2286,42 +2364,44 @@ EXPORT_SYMBOL(blk_insert_request); + * unmapping. + */ + int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf, +- unsigned int len) ++ unsigned long len) + { +- unsigned long uaddr; +- struct bio *bio; +- int reading; ++ unsigned long bytes_read = 0; ++ int ret; + + if (len > (q->max_hw_sectors << 9)) + return -EINVAL; + if (!len || !ubuf) + return -EINVAL; + +- reading = rq_data_dir(rq) == READ; ++ while (bytes_read != len) { ++ unsigned long map_len, end, start; + +- /* +- * if alignment requirement is satisfied, map in user pages for +- * direct dma. else, set up kernel bounce buffers +- */ +- uaddr = (unsigned long) ubuf; +- if (!(uaddr & queue_dma_alignment(q)) && !(len & queue_dma_alignment(q))) +- bio = bio_map_user(q, NULL, uaddr, len, reading); +- else +- bio = bio_copy_user(q, uaddr, len, reading); ++ map_len = min_t(unsigned long, len - bytes_read, BIO_MAX_SIZE); ++ end = ((unsigned long)ubuf + map_len + PAGE_SIZE - 1) ++ >> PAGE_SHIFT; ++ start = (unsigned long)ubuf >> PAGE_SHIFT; + +- if (!IS_ERR(bio)) { +- rq->bio = rq->biotail = bio; +- blk_rq_bio_prep(q, rq, bio); ++ /* ++ * A bad offset could cause us to require BIO_MAX_PAGES + 1 ++ * pages. If this happens we just lower the requested ++ * mapping len by a page so that we can fit ++ */ ++ if (end - start > BIO_MAX_PAGES) ++ map_len -= PAGE_SIZE; + +- rq->buffer = rq->data = NULL; +- rq->data_len = len; +- return 0; ++ ret = __blk_rq_map_user(q, rq, ubuf, map_len); ++ if (ret < 0) ++ goto unmap_rq; ++ bytes_read += ret; ++ ubuf += ret; + } + +- /* +- * bio is the err-ptr +- */ +- return PTR_ERR(bio); ++ rq->buffer = rq->data = NULL; ++ return 0; ++unmap_rq: ++ blk_rq_unmap_user(rq); ++ return ret; + } + + EXPORT_SYMBOL(blk_rq_map_user); +@@ -2347,7 +2427,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; + +@@ -2361,10 +2441,15 @@ int blk_rq_map_user_iov(request_queue_t + if (IS_ERR(bio)) + return PTR_ERR(bio); + +- rq->bio = rq->biotail = bio; ++ if (bio->bi_size != len) { ++ bio_endio(bio, bio->bi_size, 0); ++ bio_unmap_user(bio); ++ return -EINVAL; ++ } ++ ++ bio_get(bio); + blk_rq_bio_prep(q, rq, bio); + rq->buffer = rq->data = NULL; +- rq->data_len = bio->bi_size; + return 0; + } + +@@ -2372,23 +2457,26 @@ EXPORT_SYMBOL(blk_rq_map_user_iov); + + /** + * blk_rq_unmap_user - unmap a request with user data +- * @bio: bio to be unmapped +- * @ulen: length of user buffer ++ * @rq: rq to be unmapped + * + * Description: +- * Unmap a bio previously mapped by blk_rq_map_user(). ++ * Unmap a rq previously mapped by blk_rq_map_user(). ++ * rq->bio must be set to the original head of the request. + */ +-int blk_rq_unmap_user(struct bio *bio, unsigned int ulen) ++int blk_rq_unmap_user(struct request *rq) + { +- int ret = 0; ++ struct bio *bio, *mapped_bio; + +- if (bio) { +- if (bio_flagged(bio, BIO_USER_MAPPED)) +- bio_unmap_user(bio); ++ while ((bio = rq->bio)) { ++ if (bio_flagged(bio, BIO_BOUNCED)) ++ mapped_bio = bio->bi_private; + else +- ret = bio_uncopy_user(bio); +- } ++ mapped_bio = bio; + ++ __blk_rq_unmap_user(mapped_bio); ++ rq->bio = bio->bi_next; ++ bio_put(bio); ++ } + return 0; + } + +@@ -2419,11 +2507,8 @@ int blk_rq_map_kern(request_queue_t *q, + if (rq_data_dir(rq) == WRITE) + bio->bi_rw |= (1 << BIO_RW); + +- rq->bio = rq->biotail = bio; + blk_rq_bio_prep(q, rq, bio); +- + rq->buffer = rq->data = NULL; +- rq->data_len = len; + return 0; + } + +@@ -3429,6 +3514,7 @@ void blk_rq_bio_prep(request_queue_t *q, + rq->hard_cur_sectors = rq->current_nr_sectors; + rq->hard_nr_sectors = rq->nr_sectors = bio_sectors(bio); + rq->buffer = bio_data(bio); ++ rq->data_len = bio->bi_size; + + rq->bio = rq->biotail = bio; + } +diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c +index 24f7af9..71f66dd 100644 +--- a/block/scsi_ioctl.c ++++ b/block/scsi_ioctl.c +@@ -226,7 +226,6 @@ static int sg_io(struct file *file, requ + unsigned long start_time; + int writing = 0, ret = 0; + struct request *rq; +- struct bio *bio; + char sense[SCSI_SENSE_BUFFERSIZE]; + unsigned char cmd[BLK_MAX_CDB]; + +@@ -258,30 +257,6 @@ static int sg_io(struct file *file, requ + if (!rq) + return -ENOMEM; + +- if (hdr->iovec_count) { +- const int size = sizeof(struct sg_iovec) * hdr->iovec_count; +- struct sg_iovec *iov; +- +- iov = kmalloc(size, GFP_KERNEL); +- if (!iov) { +- ret = -ENOMEM; +- goto out; +- } +- +- if (copy_from_user(iov, hdr->dxferp, size)) { +- kfree(iov); +- ret = -EFAULT; +- goto out; +- } +- +- ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count); +- kfree(iov); +- } else if (hdr->dxfer_len) +- ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); +- +- if (ret) +- goto out; +- + /* + * fill in request structure + */ +@@ -295,7 +270,6 @@ static int sg_io(struct file *file, requ + rq->sense_len = 0; + + rq->flags |= REQ_BLOCK_PC; +- bio = rq->bio; + + /* + * bounce this after holding a reference to the original bio, it's +@@ -310,6 +284,31 @@ static int sg_io(struct file *file, requ + if (!rq->timeout) + rq->timeout = BLK_DEFAULT_TIMEOUT; + ++ if (hdr->iovec_count) { ++ const int size = sizeof(struct sg_iovec) * hdr->iovec_count; ++ struct sg_iovec *iov; ++ ++ iov = kmalloc(size, GFP_KERNEL); ++ if (!iov) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (copy_from_user(iov, hdr->dxferp, size)) { ++ kfree(iov); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ 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); ++ ++ if (ret) ++ goto out; ++ + rq->retries = 0; + + start_time = jiffies; +@@ -340,7 +339,7 @@ static int sg_io(struct file *file, requ + hdr->sb_len_wr = len; + } + +- if (blk_rq_unmap_user(bio, hdr->dxfer_len)) ++ if (blk_rq_unmap_user(rq)) + ret = -EFAULT; + + /* may not have succeeded, but output values written to control +diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c +index e866df0..3588e76 100644 +--- a/drivers/cdrom/cdrom.c ++++ b/drivers/cdrom/cdrom.c +@@ -2133,16 +2133,14 @@ static int cdrom_read_cdda_bpc(struct cd + rq->timeout = 60 * HZ; + bio = rq->bio; + +- if (rq->bio) +- blk_queue_bounce(q, &rq->bio); +- + if (blk_execute_rq(q, cdi->disk, rq, 0)) { + struct request_sense *s = rq->sense; + ret = -EIO; + cdi->last_sense = s->sense_key; + } + +- if (blk_rq_unmap_user(bio, len)) ++ rq->bio = bio; ++ if (blk_rq_unmap_user(rq)) + ret = -EFAULT; + + if (ret) +diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +index 3c606cf..c9a573b 100644 +--- a/drivers/scsi/Kconfig ++++ b/drivers/scsi/Kconfig +@@ -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 && 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 +@@ -890,6 +897,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 +@@ -1827,6 +1848,16 @@ config ZFCP + called zfcp. If you want to compile it as a module, say M here + and read <file:Documentation/modules.txt>. + ++config SCSI_SRP ++ tristate "SCSI RDMA Protocol helper library" ++ depends on SCSI && PCI ++ select SCSI_TGT ++ 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 --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile +index 320e765..0779523 100644 +--- a/drivers/scsi/Makefile ++++ b/drivers/scsi/Makefile +@@ -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 + +@@ -122,6 +123,7 @@ 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_SATA_AHCI) += libata.o ahci.o + obj-$(CONFIG_SCSI_SATA_SVW) += libata.o sata_svw.o +@@ -155,6 +157,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 --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c +index 5881079..0b4c783 100644 +--- a/drivers/scsi/hosts.c ++++ b/drivers/scsi/hosts.c +@@ -263,6 +263,10 @@ static void scsi_host_dev_release(struct + kthread_stop(shost->ehandler); + 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 --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile +index 4e247b6..6ac0633 100644 +--- a/drivers/scsi/ibmvscsi/Makefile ++++ b/drivers/scsi/ibmvscsi/Makefile +@@ -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 --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c +new file mode 100644 +index 0000000..73fcfca +--- /dev/null ++++ b/drivers/scsi/ibmvscsi/ibmvstgt.c +@@ -0,0 +1,958 @@ ++/* ++ * IBM eServer i/pSeries Virtual SCSI Target Driver ++ * Copyright (C) 2003-2005 Dave Boutcher (boutcher@xxxxxxxxxx) IBM Corp. ++ * Santiago Leon (santil@xxxxxxxxxx) IBM Corp. ++ * Linda Xie (lxie@xxxxxxxxxx) IBM Corp. ++ * ++ * Copyright (C) 2005-2006 FUJITA Tomonori <tomof@xxxxxxx> ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ * USA ++ */ ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <scsi/scsi.h> ++#include <scsi/scsi_host.h> ++#include <scsi/scsi_tgt.h> ++#include <scsi/libsrp.h> ++#include <asm/hvcall.h> ++#include <asm/iommu.h> ++#include <asm/prom.h> ++#include <asm/vio.h> ++ ++#include "ibmvscsi.h" ++ ++#define INITIAL_SRP_LIMIT 16 ++#define DEFAULT_MAX_SECTORS 512 ++ ++#define TGT_NAME "ibmvstgt" ++ ++/* ++ * Hypervisor calls. ++ */ ++#define h_copy_rdma(l, sa, sb, da, db) \ ++ plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db) ++#define h_send_crq(ua, l, h) \ ++ plpar_hcall_norets(H_SEND_CRQ, ua, l, h) ++#define h_reg_crq(ua, tok, sz)\ ++ plpar_hcall_norets(H_REG_CRQ, ua, tok, sz); ++#define h_free_crq(ua) \ ++ plpar_hcall_norets(H_FREE_CRQ, ua); ++ ++/* 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...) ++ ++struct vio_port { ++ struct vio_dev *dma_dev; ++ ++ struct crq_queue crq_queue; ++ struct work_struct crq_work; ++ ++ unsigned long liobn; ++ unsigned long riobn; ++}; ++ ++static struct workqueue_struct *vtgtd; ++ ++/* ++ * These are fixed for the system and come from the Open Firmware device tree. ++ * We just store them here to save getting them every time. ++ */ ++static char system_id[64] = ""; ++static char partition_name[97] = "UNKNOWN"; ++static unsigned int partition_number = -1; ++ ++static struct vio_port *target_to_port(struct srp_target *target) ++{ ++ return (struct vio_port *) target->ldata; ++} ++ ++static inline union viosrp_iu *vio_iu(struct iu_entry *iue) ++{ ++ return (union viosrp_iu *) (iue->sbuf->buf); ++} ++ ++static int send_iu(struct iu_entry *iue, uint64_t length, uint8_t format) ++{ ++ struct srp_target *target = iue->target; ++ struct vio_port *vport = target_to_port(target); ++ long rc, rc1; ++ union { ++ struct viosrp_crq cooked; ++ uint64_t raw[2]; ++ } crq; ++ ++ /* First copy the SRP */ ++ rc = h_copy_rdma(length, vport->liobn, iue->sbuf->dma, ++ vport->riobn, iue->remote_token); ++ ++ if (rc) ++ eprintk("Error %ld transferring data\n", rc); ++ ++ crq.cooked.valid = 0x80; ++ crq.cooked.format = format; ++ crq.cooked.reserved = 0x00; ++ crq.cooked.timeout = 0x00; ++ crq.cooked.IU_length = length; ++ crq.cooked.IU_data_ptr = vio_iu(iue)->srp.rsp.tag; ++ ++ if (rc == 0) ++ crq.cooked.status = 0x99; /* Just needs to be non-zero */ ++ else ++ crq.cooked.status = 0x00; ++ ++ rc1 = h_send_crq(vport->dma_dev->unit_address, crq.raw[0], crq.raw[1]); ++ ++ if (rc1) { ++ eprintk("%ld sending response\n", rc1); ++ return rc1; ++ } ++ ++ return rc; ++} ++ ++#define SRP_RSP_SENSE_DATA_LEN 18 ++ ++static int send_rsp(struct iu_entry *iue, struct scsi_cmnd *sc, ++ unsigned char status, unsigned char asc) ++{ ++ union viosrp_iu *iu = vio_iu(iue); ++ uint64_t tag = iu->srp.rsp.tag; ++ ++ /* If the linked bit is on and status is good */ ++ if (test_bit(V_LINKED, &iue->flags) && (status == NO_SENSE)) ++ status = 0x10; ++ ++ memset(iu, 0, sizeof(struct srp_rsp)); ++ iu->srp.rsp.opcode = SRP_RSP; ++ iu->srp.rsp.req_lim_delta = 1; ++ iu->srp.rsp.tag = tag; ++ ++ if (test_bit(V_DIOVER, &iue->flags)) ++ iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER; ++ ++ iu->srp.rsp.data_in_res_cnt = 0; ++ iu->srp.rsp.data_out_res_cnt = 0; ++ ++ iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; ++ ++ iu->srp.rsp.resp_data_len = 0; ++ iu->srp.rsp.status = status; ++ if (status) { ++ uint8_t *sense = iu->srp.rsp.data; ++ ++ if (sc) { ++ iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; ++ iu->srp.rsp.sense_data_len = SCSI_SENSE_BUFFERSIZE; ++ memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE); ++ } else { ++ iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION; ++ iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; ++ iu->srp.rsp.sense_data_len = SRP_RSP_SENSE_DATA_LEN; ++ ++ /* Valid bit and 'current errors' */ ++ sense[0] = (0x1 << 7 | 0x70); ++ /* Sense key */ ++ sense[2] = status; ++ /* Additional sense length */ ++ sense[7] = 0xa; /* 10 bytes */ ++ /* Additional sense code */ ++ sense[12] = asc; ++ } ++ } ++ ++ send_iu(iue, sizeof(iu->srp.rsp) + SRP_RSP_SENSE_DATA_LEN, ++ VIOSRP_SRP_FORMAT); ++ ++ return 0; ++} ++ ++static void handle_cmd_queue(struct srp_target *target) ++{ ++ struct Scsi_Host *shost = target->shost; ++ struct iu_entry *iue; ++ struct srp_cmd *cmd; ++ unsigned long flags; ++ int err; ++ ++retry: ++ spin_lock_irqsave(&target->lock, flags); ++ ++ list_for_each_entry(iue, &target->cmd_queue, ilist) { ++ if (!test_and_set_bit(V_FLYING, &iue->flags)) { ++ spin_unlock_irqrestore(&target->lock, flags); ++ cmd = iue->sbuf->buf; ++ err = srp_cmd_queue(shost, cmd, iue, 0); ++ if (err) { ++ eprintk("cannot queue cmd %p %d\n", cmd, err); ++ srp_iu_put(iue); ++ } ++ goto retry; ++ } ++ } ++ ++ spin_unlock_irqrestore(&target->lock, flags); ++} ++ ++static int ibmvstgt_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg, ++ struct srp_direct_buf *md, int nmd, ++ enum dma_data_direction dir, unsigned int rest) ++{ ++ struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; ++ struct srp_target *target = iue->target; ++ struct vio_port *vport = target_to_port(target); ++ dma_addr_t token; ++ long err; ++ unsigned int done = 0; ++ int i, sidx, soff; ++ ++ sidx = soff = 0; ++ token = sg_dma_address(sg + sidx); ++ ++ for (i = 0; i < nmd && rest; i++) { ++ unsigned int mdone, mlen; ++ ++ mlen = min(rest, md[i].len); ++ for (mdone = 0; mlen;) { ++ int slen = min(sg_dma_len(sg + sidx) - soff, mlen); ++ ++ if (dir == DMA_TO_DEVICE) ++ err = h_copy_rdma(slen, ++ vport->riobn, ++ md[i].va + mdone, ++ vport->liobn, ++ token + soff); ++ else ++ err = h_copy_rdma(slen, ++ vport->liobn, ++ token + soff, ++ vport->riobn, ++ md[i].va + mdone); ++ ++ if (err != H_SUCCESS) { ++ eprintk("rdma error %d %d\n", dir, slen); ++ goto out; ++ } ++ ++ mlen -= slen; ++ mdone += slen; ++ soff += slen; ++ done += slen; ++ ++ if (soff == sg_dma_len(sg + sidx)) { ++ sidx++; ++ soff = 0; ++ token = sg_dma_address(sg + sidx); ++ ++ if (sidx > nsg) { ++ eprintk("out of sg %p %d %d\n", ++ iue, sidx, nsg); ++ goto out; ++ } ++ } ++ }; ++ ++ rest -= mlen; ++ } ++out: ++ ++ return 0; ++} ++ ++static int ibmvstgt_transfer_data(struct scsi_cmnd *sc, ++ void (*done)(struct scsi_cmnd *)) ++{ ++ struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; ++ int err; ++ ++ err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, ibmvstgt_rdma, 1, 1); ++ ++ done(sc); ++ ++ return err; ++} ++ ++static int ibmvstgt_cmd_done(struct scsi_cmnd *sc, ++ void (*done)(struct scsi_cmnd *)) ++{ ++ unsigned long flags; ++ struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; ++ struct srp_target *target = iue->target; ++ ++ dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]); ++ ++ spin_lock_irqsave(&target->lock, flags); ++ list_del(&iue->ilist); ++ spin_unlock_irqrestore(&target->lock, flags); ++ ++ if (sc->result != SAM_STAT_GOOD) { ++ eprintk("operation failed %p %d %x\n", ++ iue, sc->result, vio_iu(iue)->srp.cmd.cdb[0]); ++ send_rsp(iue, sc, HARDWARE_ERROR, 0x00); ++ } else ++ send_rsp(iue, sc, NO_SENSE, 0x00); ++ ++ done(sc); ++ srp_iu_put(iue); ++ return 0; ++} ++ ++int send_adapter_info(struct iu_entry *iue, ++ dma_addr_t remote_buffer, uint16_t length) ++{ ++ struct srp_target *target = iue->target; ++ struct vio_port *vport = target_to_port(target); ++ struct Scsi_Host *shost = target->shost; ++ dma_addr_t data_token; ++ struct mad_adapter_info_data *info; ++ int err; ++ ++ info = dma_alloc_coherent(target->dev, sizeof(*info), &data_token, ++ GFP_KERNEL); ++ if (!info) { ++ eprintk("bad dma_alloc_coherent %p\n", target); ++ return 1; ++ } ++ ++ /* Get remote info */ ++ err = h_copy_rdma(sizeof(*info), vport->riobn, remote_buffer, ++ vport->liobn, data_token); ++ if (err == H_SUCCESS) { ++ dprintk("Client connect: %s (%d)\n", ++ info->partition_name, info->partition_number); ++ } ++ ++ memset(info, 0, sizeof(*info)); ++ ++ strcpy(info->srp_version, "16.a"); ++ strncpy(info->partition_name, partition_name, ++ sizeof(info->partition_name)); ++ info->partition_number = partition_number; ++ info->mad_version = 1; ++ info->os_type = 2; ++ info->port_max_txu[0] = shost->hostt->max_sectors << 9; ++ ++ /* Send our info to remote */ ++ err = h_copy_rdma(sizeof(*info), vport->liobn, data_token, ++ vport->riobn, remote_buffer); ++ ++ dma_free_coherent(target->dev, sizeof(*info), info, data_token); ++ ++ if (err != H_SUCCESS) { ++ eprintk("Error sending adapter info %d\n", err); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void process_login(struct iu_entry *iue) ++{ ++ union viosrp_iu *iu = vio_iu(iue); ++ struct srp_login_rsp *rsp = &iu->srp.login_rsp; ++ uint64_t tag = iu->srp.rsp.tag; ++ ++ /* TODO handle case that requested size is wrong and ++ * buffer format is wrong ++ */ ++ memset(iu, 0, sizeof(struct srp_login_rsp)); ++ rsp->opcode = SRP_LOGIN_RSP; ++ rsp->req_lim_delta = INITIAL_SRP_LIMIT; ++ rsp->tag = tag; ++ rsp->max_it_iu_len = sizeof(union srp_iu); ++ rsp->max_ti_iu_len = sizeof(union srp_iu); ++ /* direct and indirect */ ++ rsp->buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT; ++ ++ send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT); ++} ++ ++static inline void queue_cmd(struct iu_entry *iue) ++{ ++ struct srp_target *target = iue->target; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&target->lock, flags); ++ list_add_tail(&iue->ilist, &target->cmd_queue); ++ spin_unlock_irqrestore(&target->lock, flags); ++} ++ ++static int process_tsk_mgmt(struct iu_entry *iue) ++{ ++ union viosrp_iu *iu = vio_iu(iue); ++ int fn; ++ ++ dprintk("%p %u\n", iue, iu->srp.tsk_mgmt.tsk_mgmt_func); ++ ++ switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { ++ case SRP_TSK_ABORT_TASK: ++ fn = ABORT_TASK; ++ break; ++ case SRP_TSK_ABORT_TASK_SET: ++ fn = ABORT_TASK_SET; ++ break; ++ case SRP_TSK_CLEAR_TASK_SET: ++ fn = CLEAR_TASK_SET; ++ break; ++ case SRP_TSK_LUN_RESET: ++ fn = LOGICAL_UNIT_RESET; ++ break; ++ case SRP_TSK_CLEAR_ACA: ++ fn = CLEAR_ACA; ++ break; ++ default: ++ fn = 0; ++ } ++ if (fn) ++ scsi_tgt_tsk_mgmt_request(iue->target->shost, fn, ++ iu->srp.tsk_mgmt.task_tag, ++ (struct scsi_lun *) &iu->srp.tsk_mgmt.lun, ++ iue); ++ else ++ send_rsp(iue, NULL, ILLEGAL_REQUEST, 0x20); ++ ++ return !fn; ++} ++ ++static int process_mad_iu(struct iu_entry *iue) ++{ ++ union viosrp_iu *iu = vio_iu(iue); ++ struct viosrp_adapter_info *info; ++ struct viosrp_host_config *conf; ++ ++ switch (iu->mad.empty_iu.common.type) { ++ case VIOSRP_EMPTY_IU_TYPE: ++ eprintk("%s\n", "Unsupported EMPTY MAD IU"); ++ break; ++ case VIOSRP_ERROR_LOG_TYPE: ++ eprintk("%s\n", "Unsupported ERROR LOG MAD IU"); ++ iu->mad.error_log.common.status = 1; ++ send_iu(iue, sizeof(iu->mad.error_log), VIOSRP_MAD_FORMAT); ++ break; ++ case VIOSRP_ADAPTER_INFO_TYPE: ++ info = &iu->mad.adapter_info; ++ info->common.status = send_adapter_info(iue, info->buffer, ++ info->common.length); ++ send_iu(iue, sizeof(*info), VIOSRP_MAD_FORMAT); ++ break; ++ case VIOSRP_HOST_CONFIG_TYPE: ++ conf = &iu->mad.host_config; ++ conf->common.status = 1; ++ send_iu(iue, sizeof(*conf), VIOSRP_MAD_FORMAT); ++ break; ++ default: ++ eprintk("Unknown type %u\n", iu->srp.rsp.opcode); ++ } ++ ++ return 1; ++} ++ ++static int process_srp_iu(struct iu_entry *iue) ++{ ++ union viosrp_iu *iu = vio_iu(iue); ++ int done = 1; ++ u8 opcode = iu->srp.rsp.opcode; ++ ++ switch (opcode) { ++ case SRP_LOGIN_REQ: ++ process_login(iue); ++ break; ++ case SRP_TSK_MGMT: ++ done = process_tsk_mgmt(iue); ++ break; ++ case SRP_CMD: ++ queue_cmd(iue); ++ done = 0; ++ break; ++ case SRP_LOGIN_RSP: ++ case SRP_I_LOGOUT: ++ case SRP_T_LOGOUT: ++ case SRP_RSP: ++ case SRP_CRED_REQ: ++ case SRP_CRED_RSP: ++ case SRP_AER_REQ: ++ case SRP_AER_RSP: ++ eprintk("Unsupported type %u\n", opcode); ++ break; ++ default: ++ eprintk("Unknown type %u\n", opcode); ++ } ++ ++ return done; ++} ++ ++static void process_iu(struct viosrp_crq *crq, struct srp_target *target) ++{ ++ struct vio_port *vport = target_to_port(target); ++ struct iu_entry *iue; ++ long err, done; ++ ++ iue = srp_iu_get(target); ++ if (!iue) { ++ eprintk("Error getting IU from pool, %p\n", target); ++ return; ++ } ++ ++ iue->remote_token = crq->IU_data_ptr; ++ ++ err = h_copy_rdma(crq->IU_length, vport->riobn, ++ iue->remote_token, vport->liobn, iue->sbuf->dma); ++ ++ if (err != H_SUCCESS) { ++ eprintk("%ld transferring data error %p\n", err, iue); ++ done = 1; ++ goto out; ++ } ++ ++ if (crq->format == VIOSRP_MAD_FORMAT) ++ done = process_mad_iu(iue); ++ else ++ done = process_srp_iu(iue); ++out: ++ if (done) ++ srp_iu_put(iue); ++} ++ ++static irqreturn_t ibmvstgt_interrupt(int irq, void *data) ++{ ++ struct srp_target *target = (struct srp_target *) data; ++ struct vio_port *vport = target_to_port(target); ++ ++ vio_disable_interrupts(vport->dma_dev); ++ queue_work(vtgtd, &vport->crq_work); ++ ++ return IRQ_HANDLED; ++} ++ ++static int crq_queue_create(struct crq_queue *queue, struct srp_target *target) ++{ ++ int err; ++ struct vio_port *vport = target_to_port(target); ++ ++ queue->msgs = (struct viosrp_crq *) get_zeroed_page(GFP_KERNEL); ++ if (!queue->msgs) ++ goto malloc_failed; ++ queue->size = PAGE_SIZE / sizeof(*queue->msgs); ++ ++ queue->msg_token = dma_map_single(target->dev, queue->msgs, ++ queue->size * sizeof(*queue->msgs), ++ DMA_BIDIRECTIONAL); ++ ++ if (dma_mapping_error(queue->msg_token)) ++ goto map_failed; ++ ++ err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token, ++ PAGE_SIZE); ++ ++ /* If the adapter was left active for some reason (like kexec) ++ * try freeing and re-registering ++ */ ++ if (err == H_RESOURCE) { ++ do { ++ err = h_free_crq(vport->dma_dev->unit_address); ++ } while (err == H_BUSY || H_IS_LONG_BUSY(err)); ++ ++ err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token, ++ PAGE_SIZE); ++ } ++ ++ if (err != H_SUCCESS && err != 2) { ++ eprintk("Error 0x%x opening virtual adapter\n", err); ++ goto reg_crq_failed; ++ } ++ ++ err = request_irq(vport->dma_dev->irq, &ibmvstgt_interrupt, ++ SA_INTERRUPT, "ibmvstgt", target); ++ if (err) ++ goto req_irq_failed; ++ ++ vio_enable_interrupts(vport->dma_dev); ++ ++ h_send_crq(vport->dma_dev->unit_address, 0xC001000000000000, 0); ++ ++ queue->cur = 0; ++ spin_lock_init(&queue->lock); ++ ++ return 0; ++ ++req_irq_failed: ++ do { ++ err = h_free_crq(vport->dma_dev->unit_address); ++ } while (err == H_BUSY || H_IS_LONG_BUSY(err)); ++ ++reg_crq_failed: ++ dma_unmap_single(target->dev, queue->msg_token, ++ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); ++map_failed: ++ free_page((unsigned long) queue->msgs); ++ ++malloc_failed: ++ return -ENOMEM; ++} ++ ++static void crq_queue_destroy(struct srp_target *target) ++{ ++ struct vio_port *vport = target_to_port(target); ++ struct crq_queue *queue = &vport->crq_queue; ++ int err; ++ ++ free_irq(vport->dma_dev->irq, target); ++ do { ++ err = h_free_crq(vport->dma_dev->unit_address); ++ } while (err == H_BUSY || H_IS_LONG_BUSY(err)); ++ ++ dma_unmap_single(target->dev, queue->msg_token, ++ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); ++ ++ free_page((unsigned long) queue->msgs); ++} ++ ++static void process_crq(struct viosrp_crq *crq, struct srp_target *target) ++{ ++ struct vio_port *vport = target_to_port(target); ++ dprintk("%x %x\n", crq->valid, crq->format); ++ ++ switch (crq->valid) { ++ case 0xC0: ++ /* initialization */ ++ switch (crq->format) { ++ case 0x01: ++ h_send_crq(vport->dma_dev->unit_address, ++ 0xC002000000000000, 0); ++ break; ++ case 0x02: ++ break; ++ default: ++ eprintk("Unknown format %u\n", crq->format); ++ } ++ break; ++ case 0xFF: ++ /* transport event */ ++ break; ++ case 0x80: ++ /* real payload */ ++ switch (crq->format) { ++ case VIOSRP_SRP_FORMAT: ++ case VIOSRP_MAD_FORMAT: ++ process_iu(crq, target); ++ break; ++ case VIOSRP_OS400_FORMAT: ++ case VIOSRP_AIX_FORMAT: ++ case VIOSRP_LINUX_FORMAT: ++ case VIOSRP_INLINE_FORMAT: ++ eprintk("Unsupported format %u\n", crq->format); ++ break; ++ default: ++ eprintk("Unknown format %u\n", crq->format); ++ } ++ break; ++ default: ++ eprintk("unknown message type 0x%02x!?\n", crq->valid); ++ } ++} ++ ++static inline struct viosrp_crq *next_crq(struct crq_queue *queue) ++{ ++ struct viosrp_crq *crq; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&queue->lock, flags); ++ crq = &queue->msgs[queue->cur]; ++ if (crq->valid & 0x80) { ++ if (++queue->cur == queue->size) ++ queue->cur = 0; ++ } else ++ crq = NULL; ++ spin_unlock_irqrestore(&queue->lock, flags); ++ ++ return crq; ++} ++ ++static void handle_crq(void *data) ++{ ++ struct srp_target *target = (struct srp_target *) data; ++ struct vio_port *vport = target_to_port(target); ++ struct viosrp_crq *crq; ++ int done = 0; ++ ++ while (!done) { ++ while ((crq = next_crq(&vport->crq_queue)) != NULL) { ++ process_crq(crq, target); ++ crq->valid = 0x00; ++ } ++ ++ vio_enable_interrupts(vport->dma_dev); ++ ++ crq = next_crq(&vport->crq_queue); ++ if (crq) { ++ vio_disable_interrupts(vport->dma_dev); ++ process_crq(crq, target); ++ crq->valid = 0x00; ++ } else ++ done = 1; ++ } ++ ++ handle_cmd_queue(target); ++} ++ ++ ++static int ibmvstgt_eh_abort_handler(struct scsi_cmnd *sc) ++{ ++ unsigned long flags; ++ struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; ++ struct srp_target *target = iue->target; ++ ++ dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]); ++ ++ spin_lock_irqsave(&target->lock, flags); ++ list_del(&iue->ilist); ++ spin_unlock_irqrestore(&target->lock, flags); ++ ++ srp_iu_put(iue); ++ ++ return 0; ++} ++ ++static int ibmvstgt_tsk_mgmt_response(u64 mid, int result) ++{ ++ struct iu_entry *iue = (struct iu_entry *) ((void *) mid); ++ union viosrp_iu *iu = vio_iu(iue); ++ unsigned char status, asc; ++ ++ eprintk("%p %d\n", iue, result); ++ status = NO_SENSE; ++ asc = 0; ++ ++ switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { ++ case SRP_TSK_ABORT_TASK: ++ asc = 0x14; ++ if (result) ++ status = ABORTED_COMMAND; ++ break; ++ default: ++ break; ++ } ++ ++ send_rsp(iue, NULL, status, asc); ++ srp_iu_put(iue); ++ ++ return 0; ++} ++ ++static ssize_t system_id_show(struct class_device *cdev, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%s\n", system_id); ++} ++ ++static ssize_t partition_number_show(struct class_device *cdev, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%x\n", partition_number); ++} ++ ++static ssize_t unit_address_show(struct class_device *cdev, char *buf) ++{ ++ struct Scsi_Host *shost = class_to_shost(cdev); ++ struct srp_target *target = host_to_srp_target(shost); ++ struct vio_port *vport = target_to_port(target); ++ return snprintf(buf, PAGE_SIZE, "%x\n", vport->dma_dev->unit_address); ++} ++ ++static CLASS_DEVICE_ATTR(system_id, S_IRUGO, system_id_show, NULL); ++static CLASS_DEVICE_ATTR(partition_number, S_IRUGO, partition_number_show, NULL); ++static CLASS_DEVICE_ATTR(unit_address, S_IRUGO, unit_address_show, NULL); ++ ++static struct class_device_attribute *ibmvstgt_attrs[] = { ++ &class_device_attr_system_id, ++ &class_device_attr_partition_number, ++ &class_device_attr_unit_address, ++ NULL, ++}; ++ ++static struct scsi_host_template ibmvstgt_sht = { ++ .name = TGT_NAME, ++ .module = THIS_MODULE, ++ .can_queue = INITIAL_SRP_LIMIT, ++ .sg_tablesize = SG_ALL, ++ .use_clustering = DISABLE_CLUSTERING, ++ .max_sectors = DEFAULT_MAX_SECTORS, ++ .transfer_response = ibmvstgt_cmd_done, ++ .transfer_data = ibmvstgt_transfer_data, ++ .eh_abort_handler = ibmvstgt_eh_abort_handler, ++ .tsk_mgmt_response = ibmvstgt_tsk_mgmt_response, ++ .shost_attrs = ibmvstgt_attrs, ++ .proc_name = TGT_NAME, ++}; ++ ++static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id) ++{ ++ struct Scsi_Host *shost; ++ struct srp_target *target; ++ struct vio_port *vport; ++ unsigned int *dma, dma_size; ++ int err = -ENOMEM; ++ ++ vport = kzalloc(sizeof(struct vio_port), GFP_KERNEL); ++ if (!vport) ++ return err; ++ shost = scsi_host_alloc(&ibmvstgt_sht, sizeof(struct srp_target)); ++ if (!shost) ++ goto free_vport; ++ err = scsi_tgt_alloc_queue(shost); ++ if (err) ++ goto put_host; ++ ++ target = host_to_srp_target(shost); ++ target->shost = shost; ++ vport->dma_dev = dev; ++ target->ldata = vport; ++ err = srp_target_alloc(target, &dev->dev, INITIAL_SRP_LIMIT, ++ SRP_MAX_IU_LEN); ++ if (err) ++ goto put_host; ++ ++ dma = (unsigned int *) vio_get_attribute(dev, "ibm,my-dma-window", ++ &dma_size); ++ if (!dma || dma_size != 40) { ++ eprintk("Couldn't get window property %d\n", dma_size); ++ err = -EIO; ++ goto free_srp_target; ++ } ++ vport->liobn = dma[0]; ++ vport->riobn = dma[5]; ++ ++ INIT_WORK(&vport->crq_work, handle_crq, target); ++ ++ err = crq_queue_create(&vport->crq_queue, target); ++ if (err) ++ goto free_srp_target; ++ ++ err = scsi_add_host(shost, target->dev); ++ if (err) ++ goto destroy_queue; ++ return 0; ++ ++destroy_queue: ++ crq_queue_destroy(target); ++free_srp_target: ++ srp_target_free(target); ++put_host: ++ scsi_host_put(shost); ++free_vport: ++ kfree(vport); ++ return err; ++} ++ ++static int ibmvstgt_remove(struct vio_dev *dev) ++{ ++ struct srp_target *target = (struct srp_target *) dev->dev.driver_data; ++ struct Scsi_Host *shost = target->shost; ++ struct vio_port *vport = target->ldata; ++ ++ crq_queue_destroy(target); ++ scsi_remove_host(shost); ++ scsi_tgt_free_queue(shost); ++ srp_target_free(target); ++ kfree(vport); ++ scsi_host_put(shost); ++ return 0; ++} ++ ++static struct vio_device_id ibmvstgt_device_table[] __devinitdata = { ++ {"v-scsi-host", "IBM,v-scsi-host"}, ++ {"",""} ++}; ++ ++MODULE_DEVICE_TABLE(vio, ibmvstgt_device_table); ++ ++static struct vio_driver ibmvstgt_driver = { ++ .id_table = ibmvstgt_device_table, ++ .probe = ibmvstgt_probe, ++ .remove = ibmvstgt_remove, ++ .driver = { ++ .name = "ibmvscsis", ++ .owner = THIS_MODULE, ++ } ++}; ++ ++static int get_system_info(void) ++{ ++ struct device_node *rootdn; ++ const char *id, *model, *name; ++ unsigned int *num; ++ ++ rootdn = find_path_device("/"); ++ if (!rootdn) ++ return -ENOENT; ++ ++ model = get_property(rootdn, "model", NULL); ++ id = get_property(rootdn, "system-id", NULL); ++ if (model && id) ++ snprintf(system_id, sizeof(system_id), "%s-%s", model, id); ++ ++ name = get_property(rootdn, "ibm,partition-name", NULL); ++ if (name) ++ strncpy(partition_name, name, sizeof(partition_name)); ++ ++ num = (unsigned int *) get_property(rootdn, "ibm,partition-no", NULL); ++ if (num) ++ partition_number = *num; ++ ++ return 0; ++} ++ ++static int ibmvstgt_init(void) ++{ ++ int err = -ENOMEM; ++ ++ printk("IBM eServer i/pSeries Virtual SCSI Target Driver\n"); ++ ++ vtgtd = create_workqueue("ibmvtgtd"); ++ if (!vtgtd) ++ return err; ++ ++ err = get_system_info(); ++ if (err) ++ goto destroy_wq; ++ ++ err = vio_register_driver(&ibmvstgt_driver); ++ if (err) ++ goto destroy_wq; ++ ++ return 0; ++ ++destroy_wq: ++ destroy_workqueue(vtgtd); ++ return err; ++} ++ ++static void ibmvstgt_exit(void) ++{ ++ printk("Unregister IBM virtual SCSI driver\n"); ++ ++ destroy_workqueue(vtgtd); ++ vio_unregister_driver(&ibmvstgt_driver); ++} ++ ++MODULE_DESCRIPTION("IBM Virtual SCSI Target"); ++MODULE_AUTHOR("Dave Boutcher"); ++MODULE_LICENSE("GPL"); ++ ++module_init(ibmvstgt_init); ++module_exit(ibmvstgt_exit); +diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c +new file mode 100644 +index 0000000..ddb4ef5 +--- /dev/null ++++ b/drivers/scsi/libsrp.c +@@ -0,0 +1,442 @@ ++/* ++ * SCSI RDAM Protocol lib functions ++ * ++ * Copyright (C) 2006 FUJITA Tomonori <tomof@xxxxxxx> ++ * ++ * 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 <linux/err.h> ++#include <linux/kfifo.h> ++#include <linux/scatterlist.h> ++#include <linux/dma-mapping.h> ++#include <linux/pci.h> ++#include <scsi/scsi.h> ++#include <scsi/scsi_cmnd.h> ++#include <scsi/scsi_tcq.h> ++#include <scsi/scsi_tgt.h> ++#include <scsi/srp.h> ++#include <scsi/libsrp.h> ++ ++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 *)); ++ if (!iue) ++ return iue; ++ iue->target = target; ++ 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 srp_direct_data(struct scsi_cmnd *sc, struct srp_direct_buf *md, ++ enum dma_data_direction dir, srp_rdma_t rdma_io, ++ int dma_map, int ext_desc) ++{ ++ struct iu_entry *iue = NULL; ++ struct scatterlist *sg = NULL; ++ int err, nsg = 0, len; ++ ++ if (dma_map) { ++ iue = (struct iu_entry *) sc->SCp.ptr; ++ sg = sc->request_buffer; ++ ++ dprintk("%p %u %u %d\n", iue, sc->request_bufflen, ++ md->len, sc->use_sg); ++ ++ nsg = dma_map_sg(iue->target->dev, sg, sc->use_sg, ++ DMA_BIDIRECTIONAL); ++ if (!nsg) { ++ printk("fail to map %p %d\n", iue, sc->use_sg); ++ return 0; ++ } ++ len = min(sc->request_bufflen, md->len); ++ } else ++ len = md->len; ++ ++ err = rdma_io(sc, sg, nsg, md, 1, dir, len); ++ ++ if (dma_map) ++ dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL); ++ ++ return err; ++} ++ ++static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, ++ struct srp_indirect_buf *id, ++ enum dma_data_direction dir, srp_rdma_t rdma_io, ++ int dma_map, int ext_desc) ++{ ++ struct iu_entry *iue = NULL; ++ struct srp_direct_buf *md = NULL; ++ struct scatterlist dummy, *sg = NULL; ++ dma_addr_t token = 0; ++ long err; ++ unsigned int done = 0; ++ int nmd, nsg = 0, len; ++ ++ if (dma_map || ext_desc) { ++ iue = (struct iu_entry *) sc->SCp.ptr; ++ sg = sc->request_buffer; ++ ++ dprintk("%p %u %u %d %d\n", ++ iue, sc->request_bufflen, id->len, ++ cmd->data_in_desc_cnt, cmd->data_out_desc_cnt); ++ } ++ ++ nmd = id->table_desc.len / sizeof(struct srp_direct_buf); ++ ++ 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; ++ } ++ ++ if (ext_desc && dma_map) { ++ md = dma_alloc_coherent(iue->target->dev, id->table_desc.len, ++ &token, GFP_KERNEL); ++ if (!md) { ++ eprintk("Can't get dma memory %u\n", id->table_desc.len); ++ return -ENOMEM; ++ } ++ ++ sg_init_one(&dummy, md, id->table_desc.len); ++ sg_dma_address(&dummy) = token; ++ err = rdma_io(sc, &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; ++ } ++ } else { ++ eprintk("This command uses external indirect buffer\n"); ++ return -EINVAL; ++ } ++ ++rdma: ++ if (dma_map) { ++ nsg = dma_map_sg(iue->target->dev, sg, sc->use_sg, DMA_BIDIRECTIONAL); ++ if (!nsg) { ++ eprintk("fail to map %p %d\n", iue, sc->use_sg); ++ goto free_mem; ++ } ++ len = min(sc->request_bufflen, id->len); ++ } else ++ len = id->len; ++ ++ err = rdma_io(sc, sg, nsg, md, nmd, dir, len); ++ ++ if (dma_map) ++ dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL); ++ ++free_mem: ++ if (token && dma_map) ++ dma_free_coherent(iue->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; ++} ++ ++/* ++ * TODO: this can be called multiple times for a single command if it ++ * has very long data. ++ */ ++int srp_transfer_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, ++ srp_rdma_t rdma_io, int dma_map, int ext_desc) ++{ ++ struct srp_direct_buf *md; ++ struct srp_indirect_buf *id; ++ enum dma_data_direction dir; ++ int offset, err = 0; ++ u8 format; ++ ++ offset = cmd->add_cdb_len * 4; ++ ++ dir = srp_cmd_direction(cmd); ++ 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 = srp_direct_data(sc, md, dir, rdma_io, dma_map, ext_desc); ++ break; ++ case SRP_DATA_DESC_INDIRECT: ++ id = (struct srp_indirect_buf *) ++ (cmd->add_data + offset); ++ err = srp_indirect_data(sc, cmd, id, dir, rdma_io, dma_map, ++ ext_desc); ++ break; ++ default: ++ eprintk("Unknown format %d %x\n", dir, format); ++ break; ++ } ++ ++ return err; ++} ++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; ++} ++ ++int srp_cmd_queue(struct Scsi_Host *shost, struct srp_cmd *cmd, void *info, ++ u64 addr) ++{ ++ enum dma_data_direction dir; ++ struct scsi_cmnd *sc; ++ int tag, len, err; ++ ++ 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; ++ } ++ ++ dir = srp_cmd_direction(cmd); ++ len = vscsis_data_length(cmd, dir); ++ ++ dprintk("%p %x %lx %d %d %d %llx\n", info, cmd->cdb[0], ++ cmd->lun, dir, len, tag, (unsigned long long) cmd->tag); ++ ++ sc = scsi_host_get_command(shost, dir, GFP_KERNEL); ++ if (!sc) ++ return -ENOMEM; ++ ++ sc->SCp.ptr = info; ++ memcpy(sc->cmnd, cmd->cdb, MAX_COMMAND_SIZE); ++ sc->request_bufflen = len; ++ sc->request_buffer = (void *)(unsigned long)addr; ++ sc->tag = tag; ++ sc->host_scribble = (void *)shost; ++ err = scsi_tgt_queue_command(sc, (struct scsi_lun *) &cmd->lun, cmd->tag); ++ if (err) ++ scsi_host_put_command(shost, sc); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(srp_cmd_queue); ++ ++MODULE_DESCRIPTION("SCSI RDAM Protocol lib functions"); ++MODULE_AUTHOR("FUJITA Tomonori"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c +index c551bb8..1d2fbe0 100644 +--- a/drivers/scsi/scsi.c ++++ b/drivers/scsi/scsi.c +@@ -212,8 +212,7 @@ static struct scsi_host_cmd_pool scsi_cm + + static DEFINE_MUTEX(host_cmd_pool_mutex); + +-static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, +- gfp_t gfp_mask) ++struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) + { + struct scsi_cmnd *cmd; + +@@ -234,6 +233,7 @@ static struct scsi_cmnd *__scsi_get_comm + + return cmd; + } ++EXPORT_SYMBOL_GPL(__scsi_get_command); + + /* + * Function: scsi_get_command() +@@ -270,9 +270,29 @@ struct scsi_cmnd *scsi_get_command(struc + put_device(&dev->sdev_gendev); + + return cmd; +-} ++} + EXPORT_SYMBOL(scsi_get_command); + ++void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd, ++ struct device *dev) ++{ ++ 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_irqrestore(&shost->free_list_lock, flags); ++ ++ if (likely(cmd != NULL)) ++ kmem_cache_free(shost->cmd_pool->slab, cmd); ++ ++ put_device(dev); ++} ++EXPORT_SYMBOL(__scsi_put_command); ++ + /* + * Function: scsi_put_command() + * +@@ -287,26 +307,15 @@ EXPORT_SYMBOL(scsi_get_command); + void scsi_put_command(struct scsi_cmnd *cmd) + { + struct scsi_device *sdev = cmd->device; +- struct Scsi_Host *shost = sdev->host; + unsigned long flags; +- ++ + /* serious error if the command hasn't come from a device list */ + spin_lock_irqsave(&cmd->device->list_lock, flags); + BUG_ON(list_empty(&cmd->list)); + list_del_init(&cmd->list); +- spin_unlock(&cmd->device->list_lock); +- /* changing locks here, don't need to restore the irq state */ +- spin_lock(&shost->free_list_lock); +- if (unlikely(list_empty(&shost->free_list))) { +- list_add(&cmd->list, &shost->free_list); +- cmd = NULL; +- } +- spin_unlock_irqrestore(&shost->free_list_lock, flags); ++ spin_unlock_irqrestore(&cmd->device->list_lock, flags); + +- if (likely(cmd != NULL)) +- kmem_cache_free(shost->cmd_pool->slab, cmd); +- +- put_device(&sdev->sdev_gendev); ++ __scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev); + } + EXPORT_SYMBOL(scsi_put_command); + +diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c +index a0cd6de..d94ea0f 100644 +--- a/drivers/scsi/scsi_lib.c ++++ b/drivers/scsi/scsi_lib.c +@@ -804,7 +804,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; +@@ -845,7 +845,9 @@ #endif + 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; + +@@ -855,6 +857,8 @@ static void scsi_free_sgtable(struct sca + mempool_free(sgl, sgp->pool); + } + ++EXPORT_SYMBOL(scsi_free_sgtable); ++ + /* + * Function: scsi_release_buffers() + * +@@ -1687,29 +1691,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 --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c +new file mode 100644 +index 0000000..55bb961 +--- /dev/null ++++ b/drivers/scsi/scsi_tgt_if.c +@@ -0,0 +1,351 @@ ++/* ++ * SCSI target kernel/user interface functions ++ * ++ * Copyright (C) 2005 FUJITA Tomonori <tomof@xxxxxxx> ++ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx> ++ * ++ * 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 <linux/miscdevice.h> ++#include <linux/file.h> ++#include <net/tcp.h> ++#include <scsi/scsi.h> ++#include <scsi/scsi_cmnd.h> ++#include <scsi/scsi_device.h> ++#include <scsi/scsi_host.h> ++#include <scsi/scsi_tgt.h> ++#include <scsi/scsi_tgt_if.h> ++ ++#include "scsi_tgt_priv.h" ++ ++struct tgt_ring { ++ u32 tr_idx; ++ unsigned long tr_pages[TGT_RING_PAGES]; ++ spinlock_t tr_lock; ++}; ++ ++/* tx_ring : kernel->user, rx_ring : user->kernel */ ++static struct tgt_ring tx_ring, rx_ring; ++static DECLARE_WAIT_QUEUE_HEAD(tgt_poll_wait); ++ ++static inline void tgt_ring_idx_inc(struct tgt_ring *ring) ++{ ++ if (ring->tr_idx == TGT_MAX_EVENTS - 1) ++ ring->tr_idx = 0; ++ else ++ ring->tr_idx++; ++} ++ ++static struct tgt_event *tgt_head_event(struct tgt_ring *ring, u32 idx) ++{ ++ u32 pidx, off; ++ ++ pidx = idx / TGT_EVENT_PER_PAGE; ++ off = idx % TGT_EVENT_PER_PAGE; ++ ++ return (struct tgt_event *) ++ (ring->tr_pages[pidx] + sizeof(struct tgt_event) * off); ++} ++ ++static int tgt_uspace_send_event(u32 type, struct tgt_event *p) ++{ ++ struct tgt_event *ev; ++ struct tgt_ring *ring = &tx_ring; ++ unsigned long flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&ring->tr_lock, flags); ++ ++ ev = tgt_head_event(ring, ring->tr_idx); ++ if (!ev->hdr.status) ++ tgt_ring_idx_inc(ring); ++ else ++ err = -BUSY; ++ ++ spin_unlock_irqrestore(&ring->tr_lock, flags); ++ ++ if (err) ++ return err; ++ ++ memcpy(ev, p, sizeof(*ev)); ++ ev->hdr.type = type; ++ mb(); ++ ev->hdr.status = 1; ++ ++ flush_dcache_page(virt_to_page(ev)); ++ ++ wake_up_interruptible(&tgt_poll_wait); ++ ++ return 0; ++} ++ ++int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun, u64 tag) ++{ ++ struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); ++ struct tgt_event ev; ++ int err; ++ ++ memset(&ev, 0, sizeof(ev)); ++ ev.p.cmd_req.host_no = shost->host_no; ++ ev.p.cmd_req.data_len = cmd->request_bufflen; ++ memcpy(ev.p.cmd_req.scb, cmd->cmnd, sizeof(ev.p.cmd_req.scb)); ++ memcpy(ev.p.cmd_req.lun, lun, sizeof(ev.p.cmd_req.lun)); ++ ev.p.cmd_req.attribute = cmd->tag; ++ ev.p.cmd_req.uaddr = (unsigned long)cmd->request_buffer; ++ ev.p.cmd_req.tag = tag; ++ ++ dprintk("%p %d %u %x %llx\n", cmd, shost->host_no, ++ ev.p.cmd_req.data_len, cmd->tag, ++ (unsigned long long) ev.p.cmd_req.tag); ++ ++ err = tgt_uspace_send_event(TGT_KEVENT_CMD_REQ, &ev); ++ if (err) ++ eprintk("tx buf is full, could not send\n"); ++ ++ return err; ++} ++ ++int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 tag) ++{ ++ struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); ++ struct tgt_event ev; ++ int err; ++ ++ memset(&ev, 0, sizeof(ev)); ++ ev.p.cmd_done.host_no = shost->host_no; ++ ev.p.cmd_done.tag = tag; ++ ev.p.cmd_done.result = cmd->result; ++ ++ dprintk("%p %d %llu %u %x\n", cmd, shost->host_no, ++ (unsigned long long) ev.p.cmd_req.tag, ++ ev.p.cmd_req.data_len, cmd->tag); ++ ++ err = tgt_uspace_send_event(TGT_KEVENT_CMD_DONE, &ev); ++ if (err) ++ eprintk("tx buf is full, could not send\n"); ++ ++ return err; ++} ++ ++int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, ++ struct scsi_lun *scsilun, void *data) ++{ ++ struct tgt_event ev; ++ int err; ++ ++ memset(&ev, 0, sizeof(ev)); ++ ev.p.tsk_mgmt_req.host_no = host_no; ++ ev.p.tsk_mgmt_req.function = function; ++ ev.p.tsk_mgmt_req.tag = tag; ++ memcpy(ev.p.tsk_mgmt_req.lun, scsilun, sizeof(ev.p.tsk_mgmt_req.lun)); ++ ev.p.tsk_mgmt_req.mid = (u64) (unsigned long) data; ++ ++ dprintk("%d %x %llx %llx\n", host_no, function, (unsigned long long) tag, ++ (unsigned long long) ev.p.tsk_mgmt_req.mid); ++ ++ err = tgt_uspace_send_event(TGT_KEVENT_TSK_MGMT_REQ, &ev); ++ if (err) ++ eprintk("tx buf is full, could not send\n"); ++ ++ return err; ++} ++ ++static int event_recv_msg(struct tgt_event *ev) ++{ ++ int err = 0; ++ ++ switch (ev->hdr.type) { ++ case TGT_UEVENT_CMD_RSP: ++ err = scsi_tgt_kspace_exec(ev->p.cmd_rsp.host_no, ++ ev->p.cmd_rsp.tag, ++ ev->p.cmd_rsp.result, ++ ev->p.cmd_rsp.len, ++ ev->p.cmd_rsp.uaddr, ++ ev->p.cmd_rsp.rw); ++ break; ++ case TGT_UEVENT_TSK_MGMT_RSP: ++ err = scsi_tgt_kspace_tsk_mgmt(ev->p.tsk_mgmt_rsp.host_no, ++ ev->p.tsk_mgmt_rsp.mid, ++ ev->p.tsk_mgmt_rsp.result); ++ break; ++ default: ++ eprintk("unknown type %d\n", ev->hdr.type); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static ssize_t tgt_write(struct file *file, const char __user * buffer, ++ size_t count, loff_t * ppos) ++{ ++ struct tgt_event *ev; ++ struct tgt_ring *ring = &rx_ring; ++ ++ while (1) { ++ ev = tgt_head_event(ring, ring->tr_idx); ++ /* do we need this? */ ++ flush_dcache_page(virt_to_page(ev)); ++ ++ if (!ev->hdr.status) ++ break; ++ ++ tgt_ring_idx_inc(ring); ++ event_recv_msg(ev); ++ ev->hdr.status = 0; ++ }; ++ ++ return count; ++} ++ ++static unsigned int tgt_poll(struct file * file, struct poll_table_struct *wait) ++{ ++ struct tgt_event *ev; ++ struct tgt_ring *ring = &tx_ring; ++ unsigned long flags; ++ unsigned int mask = 0; ++ u32 idx; ++ ++ poll_wait(file, &tgt_poll_wait, wait); ++ ++ spin_lock_irqsave(&ring->tr_lock, flags); ++ ++ idx = ring->tr_idx ? ring->tr_idx - 1 : TGT_MAX_EVENTS - 1; ++ ev = tgt_head_event(ring, idx); ++ if (ev->hdr.status) ++ mask |= POLLIN | POLLRDNORM; ++ ++ spin_unlock_irqrestore(&ring->tr_lock, flags); ++ ++ return mask; ++} ++ ++static int uspace_ring_map(struct vm_area_struct *vma, unsigned long addr, ++ struct tgt_ring *ring) ++{ ++ int i, err; ++ ++ for (i = 0; i < TGT_RING_PAGES; i++) { ++ struct page *page = virt_to_page(ring->tr_pages[i]); ++ err = vm_insert_page(vma, addr, page); ++ if (err) ++ return err; ++ addr += PAGE_SIZE; ++ } ++ ++ return 0; ++} ++ ++static int tgt_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ unsigned long addr; ++ int err; ++ ++ if (vma->vm_pgoff) ++ return -EINVAL; ++ ++ if (vma->vm_end - vma->vm_start != TGT_RING_SIZE * 2) { ++ eprintk("mmap size must be %lu, not %lu \n", ++ TGT_RING_SIZE * 2, vma->vm_end - vma->vm_start); ++ return -EINVAL; ++ } ++ ++ addr = vma->vm_start; ++ err = uspace_ring_map(vma, addr, &tx_ring); ++ if (err) ++ return err; ++ err = uspace_ring_map(vma, addr + TGT_RING_SIZE, &rx_ring); ++ ++ return err; ++} ++ ++static int tgt_open(struct inode *inode, struct file *file) ++{ ++ tx_ring.tr_idx = rx_ring.tr_idx = 0; ++ ++ return 0; ++} ++ ++static struct file_operations tgt_fops = { ++ .owner = THIS_MODULE, ++ .open = tgt_open, ++ .poll = tgt_poll, ++ .write = tgt_write, ++ .mmap = tgt_mmap, ++}; ++ ++static struct miscdevice tgt_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "tgt", ++ .fops = &tgt_fops, ++}; ++ ++static void tgt_ring_exit(struct tgt_ring *ring) ++{ ++ int i; ++ ++ for (i = 0; i < TGT_RING_PAGES; i++) ++ free_page(ring->tr_pages[i]); ++} ++ ++static int tgt_ring_init(struct tgt_ring *ring) ++{ ++ int i; ++ ++ spin_lock_init(&ring->tr_lock); ++ ++ for (i = 0; i < TGT_RING_PAGES; i++) { ++ ring->tr_pages[i] = get_zeroed_page(GFP_KERNEL); ++ if (!ring->tr_pages[i]) { ++ eprintk("out of memory\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ return 0; ++} ++ ++void scsi_tgt_if_exit(void) ++{ ++ tgt_ring_exit(&tx_ring); ++ tgt_ring_exit(&rx_ring); ++ misc_deregister(&tgt_miscdev); ++} ++ ++int scsi_tgt_if_init(void) ++{ ++ int err; ++ ++ err = tgt_ring_init(&tx_ring); ++ if (err) ++ return err; ++ ++ err = tgt_ring_init(&rx_ring); ++ if (err) ++ goto free_tx_ring; ++ ++ err = misc_register(&tgt_miscdev); ++ if (err) ++ goto free_rx_ring; ++ ++ return 0; ++free_rx_ring: ++ tgt_ring_exit(&rx_ring); ++free_tx_ring: ++ tgt_ring_exit(&tx_ring); ++ ++ return err; ++} +diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c +new file mode 100644 +index 0000000..0211b5c +--- /dev/null ++++ b/drivers/scsi/scsi_tgt_lib.c +@@ -0,0 +1,741 @@ ++/* ++ * SCSI target lib functions ++ * ++ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx> ++ * Copyright (C) 2005 FUJITA Tomonori <tomof@xxxxxxx> ++ * ++ * 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 <linux/blkdev.h> ++#include <linux/hash.h> ++#include <linux/module.h> ++#include <linux/pagemap.h> ++#include <scsi/scsi.h> ++#include <scsi/scsi_cmnd.h> ++#include <scsi/scsi_device.h> ++#include <scsi/scsi_host.h> ++#include <scsi/scsi_tgt.h> ++#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 list_head hash_list; ++ struct request *rq; ++ u64 tag; ++ ++ void *buffer; ++ unsigned bufflen; ++}; ++ ++#define TGT_HASH_ORDER 4 ++#define cmd_hashfn(tag) hash_long((unsigned long) (tag), 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; ++}; ++ ++/* ++ * 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; ++ struct scsi_tgt_cmd *tcmd; ++ ++ /* Bail if we can't get a reference to the device */ ++ if (!get_device(&shost->shost_gendev)) ++ return NULL; ++ ++ tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC); ++ if (!tcmd) ++ goto put_dev; ++ ++ rq = blk_get_request(shost->uspace_req_q, write, gfp_mask); ++ if (!rq) ++ goto free_tcmd; ++ ++ 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_BLOCK_PC | REQ_SPECIAL; ++ rq->end_io_data = tcmd; ++ ++ bio_list_init(&tcmd->xfer_list); ++ bio_list_init(&tcmd->xfer_done_list); ++ tcmd->rq = rq; ++ ++ return cmd; ++ ++release_rq: ++ blk_put_request(rq); ++free_tcmd: ++ kmem_cache_free(scsi_tgt_cmd_cache, tcmd); ++put_dev: ++ put_device(&shost->shost_gendev); ++ return NULL; ++ ++} ++EXPORT_SYMBOL_GPL(scsi_host_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; ++ struct scsi_tgt_cmd *tcmd = rq->end_io_data; ++ unsigned long flags; ++ ++ kmem_cache_free(scsi_tgt_cmd_cache, tcmd); ++ ++ spin_lock_irqsave(q->queue_lock, flags); ++ __blk_put_request(q, rq); ++ spin_unlock_irqrestore(q->queue_lock, flags); ++ ++ __scsi_put_command(shost, cmd, &shost->shost_gendev); ++} ++EXPORT_SYMBOL_GPL(scsi_host_put_command); ++ ++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 cmd_hashlist_del(struct scsi_cmnd *cmd) ++{ ++ struct request_queue *q = cmd->request->q; ++ struct scsi_tgt_queuedata *qdata = q->queuedata; ++ unsigned long flags; ++ struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; ++ ++ spin_lock_irqsave(&qdata->cmd_hash_lock, flags); ++ list_del(&tcmd->hash_list); ++ spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); ++} ++ ++static void scsi_tgt_cmd_destroy(void *data) ++{ ++ struct scsi_cmnd *cmd = data; ++ struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; ++ ++ dprintk("cmd %p %d %lu\n", cmd, cmd->sc_data_direction, ++ rq_data_dir(cmd->request)); ++ /* ++ * We fix rq->cmd_flags here since when we told bio_map_user ++ * to write vm for WRITE commands, blk_rq_bio_prep set ++ * rq_data_dir the flags to READ. ++ */ ++ if (cmd->sc_data_direction == DMA_TO_DEVICE) ++ cmd->request->flags |= REQ_RW; ++ else ++ cmd->request->flags &= ~REQ_RW; ++ ++ scsi_unmap_user_pages(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, ++ u64 tag) ++{ ++ struct scsi_tgt_queuedata *qdata = rq->q->queuedata; ++ unsigned long flags; ++ struct list_head *head; ++ ++ tcmd->tag = tag; ++ spin_lock_irqsave(&qdata->cmd_hash_lock, flags); ++ head = &qdata->cmd_hash[cmd_hashfn(tag)]; ++ list_add(&tcmd->hash_list, head); ++ spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); ++} ++ ++/* ++ * 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); ++ ++ return 0; ++ ++cleanup_queue: ++ blk_cleanup_queue(q); ++ return err; ++} ++EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue); ++ ++void scsi_tgt_free_queue(struct Scsi_Host *shost) ++{ ++ int i; ++ unsigned long flags; ++ struct request_queue *q = shost->uspace_req_q; ++ struct scsi_cmnd *cmd; ++ struct scsi_tgt_queuedata *qdata = q->queuedata; ++ struct scsi_tgt_cmd *tcmd, *n; ++ LIST_HEAD(cmds); ++ ++ spin_lock_irqsave(&qdata->cmd_hash_lock, flags); ++ ++ for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) { ++ list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i], ++ hash_list) { ++ list_del(&tcmd->hash_list); ++ list_add(&tcmd->hash_list, &cmds); ++ } ++ } ++ ++ spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); ++ ++ while (!list_empty(&cmds)) { ++ tcmd = list_entry(cmds.next, struct scsi_tgt_cmd, hash_list); ++ list_del(&tcmd->hash_list); ++ cmd = tcmd->rq->special; ++ ++ shost->hostt->eh_abort_handler(cmd); ++ scsi_tgt_cmd_destroy(cmd); ++ } ++} ++EXPORT_SYMBOL_GPL(scsi_tgt_free_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 ++ * @tag: unique value to identify this command for tmf ++ */ ++int scsi_tgt_queue_command(struct scsi_cmnd *cmd, struct scsi_lun *scsilun, ++ u64 tag) ++{ ++ struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; ++ int err; ++ ++ init_scsi_tgt_cmd(cmd->request, tcmd, tag); ++ err = scsi_tgt_uspace_send_cmd(cmd, scsilun, tag); ++ if (err) ++ cmd_hashlist_del(cmd); ++ ++ return err; ++} ++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, tcmd->tag); ++ 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) ++{ ++ struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; ++ int err; ++ ++ err = __scsi_tgt_transfer_response(cmd); ++ if (!err) ++ return; ++ ++ cmd->result = DID_BUS_BUSY << 16; ++ err = scsi_tgt_uspace_send_status(cmd, tcmd->tag); ++ if (err <= 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; ++ struct scsi_tgt_cmd *tcmd = rq->end_io_data; ++ 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, tcmd->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, tcmd->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 = tcmd->buffer; ++ unsigned int len = tcmd->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: ++ err = scsi_tgt_uspace_send_status(cmd, tcmd->tag); ++ if (err <= 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, tcmd->bufflen); ++ ++ scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); ++ bio_list_add(&tcmd->xfer_done_list, cmd->request->bio); ++ ++ tcmd->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, tcmd->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 *shost, struct scsi_cmnd *cmd) ++{ ++ int err; ++ ++ err = shost->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, u64 tag) ++{ ++ 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(tag)]; ++ spin_lock_irqsave(&qdata->cmd_hash_lock, flags); ++ list_for_each_entry(tcmd, head, hash_list) { ++ if (tcmd->tag == tag) { ++ rq = tcmd->rq; ++ list_del(&tcmd->hash_list); ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); ++ ++ return rq; ++} ++ ++int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, ++ unsigned long uaddr, u8 rw) ++{ ++ struct Scsi_Host *shost; ++ struct scsi_cmnd *cmd; ++ struct request *rq; ++ struct scsi_tgt_cmd *tcmd; ++ int err = 0; ++ ++ dprintk("%d %llu %d %u %lx %u\n", host_no, (unsigned long long) tag, ++ 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; ++ } ++ ++ if (!shost->uspace_req_q) { ++ printk(KERN_ERR "Not target scsi host %d\n", host_no); ++ goto done; ++ } ++ ++ rq = tgt_cmd_hash_lookup(shost->uspace_req_q, tag); ++ if (!rq) { ++ printk(KERN_ERR "Could not find tag %llu\n", ++ (unsigned long long) tag); ++ 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 ++ */ ++ tcmd = cmd->request->end_io_data; ++ tcmd->buffer = (void *)uaddr; ++ tcmd->bufflen = len; ++ cmd->result = result; ++ ++ if (!tcmd->bufflen || cmd->request_buffer) { ++ 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 = -EINVAL; ++ ++ 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 err; ++ } ++ ++ if (!shost->uspace_req_q) { ++ printk(KERN_ERR "Not target scsi host %d\n", host_no); ++ goto done; ++ } ++ ++ err = shost->hostt->tsk_mgmt_response(mid, result); ++done: ++ 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 --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h +new file mode 100644 +index 0000000..84488c5 +--- /dev/null ++++ b/drivers/scsi/scsi_tgt_priv.h +@@ -0,0 +1,25 @@ ++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(fmt, args...) ++/* #define dprintk eprintk */ ++ ++extern void scsi_tgt_if_exit(void); ++extern int scsi_tgt_if_init(void); ++ ++extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun, ++ u64 tag); ++extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 tag); ++extern int scsi_tgt_kspace_exec(int host_no, u64 tag, 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 --git a/fs/bio.c b/fs/bio.c +index 0cba08f..59f4c02 100644 +--- a/fs/bio.c ++++ b/fs/bio.c +@@ -558,10 +558,8 @@ struct bio *bio_copy_user(request_queue_ + break; + } + +- if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) { +- ret = -EINVAL; ++ if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) + break; +- } + + len -= bytes; + } +@@ -620,10 +618,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); + } + +@@ -751,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); + +@@ -766,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 --git a/include/linux/blkdev.h b/include/linux/blkdev.h +index 860e7a4..45c007d 100644 +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -608,10 +608,11 @@ extern void blk_sync_queue(struct reques + extern void __blk_stop_queue(request_queue_t *q); + extern void blk_run_queue(request_queue_t *); + extern void blk_queue_activity_fn(request_queue_t *, activity_fn *, void *); +-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_user(request_queue_t *, struct request *, void __user *, unsigned long); ++extern int blk_rq_unmap_user(struct request *); + 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 --git a/include/scsi/libsrp.h b/include/scsi/libsrp.h +new file mode 100644 +index 0000000..d143171 +--- /dev/null ++++ b/include/scsi/libsrp.h +@@ -0,0 +1,77 @@ ++#ifndef __LIBSRP_H__ ++#define __LIBSRP_H__ ++ ++#include <linux/list.h> ++#include <scsi/scsi_cmnd.h> ++#include <scsi/scsi_host.h> ++#include <scsi/srp.h> ++ ++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; ++ ++ void *ldata; ++}; ++ ++struct iu_entry { ++ struct srp_target *target; ++ ++ struct list_head ilist; ++ dma_addr_t remote_token; ++ unsigned long flags; ++ ++ struct srp_buf *sbuf; ++}; ++ ++typedef int (srp_rdma_t)(struct scsi_cmnd *, struct scatterlist *, int, ++ struct srp_direct_buf *, int, ++ enum dma_data_direction, unsigned int); ++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_queue(struct Scsi_Host *, struct srp_cmd *, void *, u64); ++extern int srp_transfer_data(struct scsi_cmnd *, struct srp_cmd *, ++ srp_rdma_t, int, int); ++ ++ ++static inline struct srp_target *host_to_srp_target(struct Scsi_Host *host) ++{ ++ return (struct srp_target *) host->hostdata; ++} ++ ++static inline int srp_cmd_direction(struct srp_cmd *cmd) ++{ ++ return (cmd->buf_fmt >> 4) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; ++} ++ ++#endif +diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h +index 7529f43..df8990e 100644 +--- a/include/scsi/scsi_cmnd.h ++++ b/include/scsi/scsi_cmnd.h +@@ -8,6 +8,7 @@ #include <linux/timer.h> + + struct request; + struct scatterlist; ++struct Scsi_Host; + struct scsi_device; + struct scsi_request; + +@@ -85,6 +86,9 @@ #define MAX_COMMAND_SIZE 16 + 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 */ + unsigned old_underflow; /* save underflow here when reusing the +@@ -148,8 +152,14 @@ #define SCSI_STATE_MLQUEUE 0x100 + + + extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); ++extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t); + extern void scsi_put_command(struct scsi_cmnd *); ++extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *, ++ struct device *); + extern void scsi_io_completion(struct scsi_cmnd *, unsigned int, unsigned int); + extern void scsi_finish_command(struct scsi_cmnd *cmd); + ++extern struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *, gfp_t); ++extern void scsi_free_sgtable(struct scatterlist *, int); ++ + #endif /* _SCSI_SCSI_CMND_H */ +diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h +index 8279929..3e1b6b6 100644 +--- a/include/scsi/scsi_host.h ++++ b/include/scsi/scsi_host.h +@@ -7,6 +7,7 @@ #include <linux/types.h> + #include <linux/workqueue.h> + #include <linux/mutex.h> + ++struct request_queue; + struct block_device; + struct completion; + struct module; +@@ -123,6 +124,39 @@ #endif + 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 +@@ -572,6 +606,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; +@@ -674,6 +714,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 --git a/include/scsi/scsi_tgt.h b/include/scsi/scsi_tgt.h +new file mode 100644 +index 0000000..4f44279 +--- /dev/null ++++ b/include/scsi/scsi_tgt.h +@@ -0,0 +1,19 @@ ++/* ++ * SCSI target definitions ++ */ ++ ++#include <linux/dma-mapping.h> ++ ++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 void scsi_tgt_free_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 *); ++extern struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *, ++ enum dma_data_direction, gfp_t); ++extern void scsi_host_put_command(struct Scsi_Host *, struct scsi_cmnd *); +diff --git a/include/scsi/scsi_tgt_if.h b/include/scsi/scsi_tgt_if.h +new file mode 100644 +index 0000000..4aad0fe +--- /dev/null ++++ b/include/scsi/scsi_tgt_if.h +@@ -0,0 +1,91 @@ ++/* ++ * SCSI target kernel/user interface ++ * ++ * Copyright (C) 2005 FUJITA Tomonori <tomof@xxxxxxx> ++ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx> ++ * ++ * 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 ++ ++/* user -> kernel */ ++#define TGT_UEVENT_CMD_RSP 0x0001 ++#define TGT_UEVENT_TSK_MGMT_RSP 0x0002 ++ ++/* kernel -> user */ ++#define TGT_KEVENT_CMD_REQ 0x1001 ++#define TGT_KEVENT_CMD_DONE 0x1002 ++#define TGT_KEVENT_TSK_MGMT_REQ 0x1003 ++ ++struct tgt_event_hdr { ++ uint16_t version; ++ uint16_t status; ++ uint16_t type; ++ uint16_t len; ++} __attribute__ ((aligned (sizeof(uint64_t)))); ++ ++struct tgt_event { ++ struct tgt_event_hdr hdr; ++ ++ union { ++ /* user-> kernel */ ++ struct { ++ int host_no; ++ uint32_t len; ++ int result; ++ aligned_u64 uaddr; ++ uint8_t rw; ++ aligned_u64 tag; ++ } cmd_rsp; ++ struct { ++ int host_no; ++ aligned_u64 mid; ++ int result; ++ } tsk_mgmt_rsp; ++ ++ ++ /* kernel -> user */ ++ struct { ++ int host_no; ++ uint32_t data_len; ++ uint8_t scb[16]; ++ uint8_t lun[8]; ++ int attribute; ++ aligned_u64 tag; ++ aligned_u64 uaddr; ++ } cmd_req; ++ struct { ++ int host_no; ++ aligned_u64 tag; ++ int result; ++ } cmd_done; ++ struct { ++ int host_no; ++ int function; ++ aligned_u64 tag; ++ uint8_t lun[8]; ++ aligned_u64 mid; ++ } tsk_mgmt_req; ++ } p; ++} __attribute__ ((aligned (sizeof(uint64_t)))); ++ ++#define TGT_RING_SIZE (1UL << 16) ++#define TGT_RING_PAGES (TGT_RING_SIZE >> PAGE_SHIFT) ++#define TGT_EVENT_PER_PAGE (PAGE_SIZE / sizeof(struct tgt_event)) ++#define TGT_MAX_EVENTS (TGT_EVENT_PER_PAGE * TGT_RING_PAGES) ++ ++#endif _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |