From de6aa12e9ccc4a951c5f2f86463725506e030b2e Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 22 Apr 2014 07:50:34 +0000 Subject: [PATCH] Merged revisions 5409-5452,5454 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5409 | bvassche | 2014-04-06 23:13:53 +0300 (Sun, 06 Apr 2014) | 4 lines scstadmin: Restore LUNs in "scstadmin -list_sessions" output Signed-off-by: Dave Butler ........ r5410 | bvassche | 2014-04-06 23:26:08 +0300 (Sun, 06 Apr 2014) | 1 line scstadmin: List keys alphabetically in the -list_sessions output ........ r5411 | vlnb | 2014-04-10 02:58:20 +0300 (Thu, 10 Apr 2014) | 10 lines vdisk_blockio: Specify REQ_SYNC for synchronous I/O requests Using READ_SYNC instead of READ increases the priority of read requests. Using WRITE_SYNC instead of REQ_WRITE increases the priority of write requests and avoids that the CFQ scheduler queues such writes waiting for further write requests. Signed-off-by: Bart Van Assche ........ r5412 | vlnb | 2014-04-10 02:59:02 +0300 (Thu, 10 Apr 2014) | 18 lines vdisk_blockio: Reenable COMPARE AND WRITE The COMPARE AND WRITE implementation has been tested as follows against an SCST vdisk_blockio device: lba=7 bdev=$bdev for ((i=0;i<4;i++)); do dd if=/dev/urandom of=b$i bs=8k count=1; done for p in "0 1" "1 2" "2 3" "3 0"; do set $p; cat b$1 b$2 >b$1$2; done dd if=/dev/urandom of=$bdev dd if=b0 of=$bdev seek=$lba md5sum $bdev for ((i=0;i<10000;i++)); do for f in b01 b12 b23 b30; do sg_compare_and_write -l $lba -i $f -n 16 -x 16384 $bdev; done; done md5sum $bdev Signed-off-by: Bart Van Assche ........ r5413 | bvassche | 2014-04-15 09:03:59 +0300 (Tue, 15 Apr 2014) | 1 line ib_srpt: Fix a sparse warning ........ r5414 | vlnb | 2014-04-16 00:26:06 +0300 (Wed, 16 Apr 2014) | 5 lines Fix READ(6)/WRITE(6) LBA in those commands is 3 bytes long, not 2. ........ r5415 | vlnb | 2014-04-16 00:30:26 +0300 (Wed, 16 Apr 2014) | 3 lines Add SYNCHRONIZE_CACHE(16) ........ r5416 | vlnb | 2014-04-16 01:04:54 +0300 (Wed, 16 Apr 2014) | 6 lines Make HEAD OF QUEUE requests sync Since the block layer has no way to specify bio as HQ, there's no choice, but to use every measure to approximate it as close as possible. ........ r5417 | vlnb | 2014-04-16 01:17:34 +0300 (Wed, 16 Apr 2014) | 3 lines Add NULLIO VERIFY ........ r5418 | vlnb | 2014-04-16 04:02:50 +0300 (Wed, 16 Apr 2014) | 3 lines REPORT SUPPORTED OPERATION CODES added ........ r5419 | vlnb | 2014-04-16 04:05:49 +0300 (Wed, 16 Apr 2014) | 3 lines Cleanup ........ r5420 | vlnb | 2014-04-16 05:24:44 +0300 (Wed, 16 Apr 2014) | 3 lines Follow up for r5418: some cleanups and fixes ........ r5421 | bvassche | 2014-04-16 09:17:19 +0300 (Wed, 16 Apr 2014) | 6 lines scst_targ: Fix a checkpatch complaint Avoid that checkpatch reports the following message: ERROR: space required before the open parenthesis '(' ........ r5422 | bvassche | 2014-04-16 09:35:19 +0300 (Wed, 16 Apr 2014) | 1 line scst_vdisk: Build fix for kernels < 2.6.36 (see also r5416) ........ r5423 | bvassche | 2014-04-16 10:30:34 +0300 (Wed, 16 Apr 2014) | 6 lines ib_srpt: Disable RDMA access by the initiator With the SRP protocol all RDMA operations are initiated by the target. Since no RDMA operations are initiated by the initiator, do not grant the initiator permission to submit RDMA reads or writes to the target. ........ r5424 | bvassche | 2014-04-16 11:01:58 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Constify two arguments of srpt_get_nexus() ........ r5425 | bvassche | 2014-04-16 11:08:50 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Clean up the code that prints the dgid during login ........ r5426 | bvassche | 2014-04-16 11:23:11 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Cache P_Key lookups ........ r5427 | bvassche | 2014-04-16 12:14:31 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Remove a superfluous assignment ........ r5428 | bvassche | 2014-04-16 12:31:56 +0300 (Wed, 16 Apr 2014) | 1 line scst_vdisk: Avoid that checkpatch complains about unnecessary line continuations ........ r5429 | bvassche | 2014-04-16 12:35:18 +0300 (Wed, 16 Apr 2014) | 1 line scst_lib: Avoid that checkpatch complains about unnecessary line continuations ........ r5430 | bvassche | 2014-04-17 09:09:41 +0300 (Thu, 17 Apr 2014) | 1 line scst_vdisk: Kernel 2.6.27 build fix ........ r5431 | bvassche | 2014-04-17 09:12:07 +0300 (Thu, 17 Apr 2014) | 5 lines scst_vdisk: Fix a kernel 2.6.27 compiler warning Avoid that the compiler reports that variables 'start_sector' and 'nr_sects' are set but not used when building against kernel 2.6.27. ........ r5432 | vlnb | 2014-04-18 03:32:51 +0300 (Fri, 18 Apr 2014) | 3 lines Update to kernels 3.14 ........ r5433 | bvassche | 2014-04-18 08:50:04 +0300 (Fri, 18 Apr 2014) | 1 line nightly build: Add kernel 3.14 build infrastructure ........ r5434 | bvassche | 2014-04-18 08:54:29 +0300 (Fri, 18 Apr 2014) | 1 line nightly build: Add kernel version 3.14.1 and update other kernel versions ........ r5435 | bvassche | 2014-04-18 08:56:35 +0300 (Fri, 18 Apr 2014) | 6 lines iscsi-scst: Fix a checkpatch warning Fix the following checkpatch 3.14 warning: Unnecessary parentheses - maybe == should be = ? ........ r5436 | bvassche | 2014-04-18 09:03:34 +0300 (Fri, 18 Apr 2014) | 6 lines scst.h: Fix a checkpatch warning Fix the following checkpatch 3.14 warning: Unnecessary space after function pointer name ........ r5437 | bvassche | 2014-04-18 09:08:07 +0300 (Fri, 18 Apr 2014) | 6 lines scst: Fix a checkpatch warning Fix the following checkpatch 3.14 warning: Unnecessary space after function pointer name ........ r5438 | bvassche | 2014-04-18 09:13:03 +0300 (Fri, 18 Apr 2014) | 6 lines scst: Fix a checkpatch 3.14 warning about whitespace Fix the following checkpatch 3.14 warning: missing space after return type ........ r5439 | bvassche | 2014-04-18 14:09:55 +0300 (Fri, 18 Apr 2014) | 1 line ib_srpt: Update README ........ r5440 | bvassche | 2014-04-18 15:04:05 +0300 (Fri, 18 Apr 2014) | 2 lines ib_srpt: Move IB/CM knowledge out of srpt_cm_req_recv() ........ r5441 | bvassche | 2014-04-18 15:08:00 +0300 (Fri, 18 Apr 2014) | 6 lines ib_srpt: Remove a superfluous check from the REQ handler ib_send_cm_rep() checks the connection state before sending a response. Hence checking ch->state before calling ib_send_cm_rep() is superfluous, so remove that check and also the locking that is no longer needed. ........ r5442 | bvassche | 2014-04-18 15:09:33 +0300 (Fri, 18 Apr 2014) | 5 lines ib_srpt: Prepare RDMA/CM support Move IB/CM members into a new struct. Report channel pointer instead of CM ID pointer in diagnostic messages. ........ r5443 | bvassche | 2014-04-18 15:10:47 +0300 (Fri, 18 Apr 2014) | 5 lines ib_srpt: Use a mutex instead of a spinlock to protect the channel list This is allowed because all CM callback functions are invoked from thread context. ........ r5444 | bvassche | 2014-04-18 15:11:18 +0300 (Fri, 18 Apr 2014) | 2 lines ib_srpt: Move the code for checking the QP timeout ........ r5445 | bvassche | 2014-04-18 15:22:30 +0300 (Fri, 18 Apr 2014) | 4 lines ib_srpt: Add RDMA/CM support Or in other words, add RoCE and iWARP support. ........ r5446 | bvassche | 2014-04-18 15:38:39 +0300 (Fri, 18 Apr 2014) | 1 line ib_srpt: Build fix for kernel versions < 3.0 ........ r5447 | bvassche | 2014-04-18 15:44:48 +0300 (Fri, 18 Apr 2014) | 1 line ib_srpt: RHEL 6.5 build fix ........ r5448 | bvassche | 2014-04-19 14:48:33 +0300 (Sat, 19 Apr 2014) | 1 line scst_vdisk, COMPARE AND WRITE: Convert a kernel warning into a SCSI sense code ........ r5449 | bvassche | 2014-04-19 14:52:34 +0300 (Sat, 19 Apr 2014) | 33 lines vdisk_blockio: Make COMPARE AND WRITE compatible with the scsi_debug driver This patch fixes the following kernel oops: BUG: unable to handle kernel paging request at ffffeae380000690 Call Trace: [] sg_miter_next+0x9/0xd0 [] sg_copy_buffer+0xa0/0x100 [] do_device_access.isra.8+0xa6/0x150 [scsi_debug] [] resp_read+0xe4/0x240 [scsi_debug] [] scsi_debug_queuecommand_lck+0x11e5/0x2060 [scsi_debug] [] scsi_debug_queuecommand+0x30/0x48 [scsi_debug] [] scsi_dispatch_cmd+0xaf/0x260 [] scsi_request_fn+0x32d/0x540 [] __blk_run_queue+0x2a/0x40 [] blk_queue_bio+0x274/0x350 [] generic_make_request+0xa8/0xf0 [] submit_bio+0x6c/0x140 [] blockio_rw_sync.isra.29+0x106/0x170 [scst_vdisk] [] vdisk_exec_caw+0xd9/0x3c0 [scst_vdisk] [] vdev_do_job+0x9e/0x320 [scst_vdisk] [] non_fileio_exec+0x57/0xd0 [scst_vdisk] [] scst_do_real_exec+0x92/0x3b0 [scst] [] scst_exec_check_blocking+0xe2/0x300 [scst] [] scst_exec_check_sn+0x17b/0x2d0 [scst] [] scst_process_active_cmd+0x431/0x770 [scst] [] scst_do_job_active+0xea/0x180 [scst] [] scst_cmd_thread+0x126/0x290 [scst] [] kthread+0xc1/0xe0 [] ret_from_fork+0x7c/0xb0 Reported-by: Sebastian Herbszt ........ r5450 | bvassche | 2014-04-20 09:24:23 +0300 (Sun, 20 Apr 2014) | 1 line iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch: Add ........ r5451 | bvassche | 2014-04-22 09:56:37 +0300 (Tue, 22 Apr 2014) | 1 line scst: Revert r5438, a whitespace-only change ........ r5452 | bvassche | 2014-04-22 10:05:21 +0300 (Tue, 22 Apr 2014) | 1 line scripts/run-regression-tests: Suppress the checkpatch warning "missing space after return type" ........ r5454 | bvassche | 2014-04-22 10:32:44 +0300 (Tue, 22 Apr 2014) | 1 line scst/README: Update the section about Linux initiator ALUA support ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5455 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/conn.c | 2 +- .../patches/put_page_callback-3.14.patch | 364 +++++++++++ .../patches/put_page_callback-3.2.57.patch | 287 +++++++++ nightly/conf/nightly.conf | 13 +- qla2x00t/qla2x00-target/Makefile_in-tree-3.14 | 5 + scripts/generate-kernel-patch | 4 +- scripts/run-regression-tests | 1 + scst/README | 39 +- scst/include/scst.h | 174 ++++-- scst/include/scst_const.h | 9 +- .../in-tree/Kconfig.drivers.Linux-3.14.patch | 13 + .../kernel/in-tree/Makefile.dev_handlers-3.14 | 14 + .../in-tree/Makefile.drivers.Linux-3.14.patch | 12 + scst/kernel/in-tree/Makefile.scst-3.14 | 13 + scst/kernel/scst_exec_req_fifo-3.14.patch | 528 ++++++++++++++++ scst/src/dev_handlers/scst_vdisk.c | 441 ++++++++++++- scst/src/scst_lib.c | 252 +++++++- scst/src/scst_pres.c | 2 +- scst/src/scst_targ.c | 213 ++++++- scst_local/in-tree/Makefile-3.14 | 2 + .../scst-0.9.10/lib/SCST/SCST.pm | 2 +- scstadmin/scstadmin.sysfs/scstadmin | 4 +- srpt/README | 61 +- srpt/patches/kernel-3.14-pre-cflags.patch | 12 + srpt/session-management.txt | 13 +- srpt/src/ib_srpt.c | 588 +++++++++++------- srpt/src/ib_srpt.h | 40 +- usr/fileio/common.c | 1 + 28 files changed, 2733 insertions(+), 376 deletions(-) create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.14.patch create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch create mode 100644 qla2x00t/qla2x00-target/Makefile_in-tree-3.14 create mode 100644 scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch create mode 100644 scst/kernel/in-tree/Makefile.dev_handlers-3.14 create mode 100644 scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch create mode 100644 scst/kernel/in-tree/Makefile.scst-3.14 create mode 100644 scst/kernel/scst_exec_req_fifo-3.14.patch create mode 100644 scst_local/in-tree/Makefile-3.14 create mode 100644 srpt/patches/kernel-3.14-pre-cflags.patch diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index ef043cffb..cceb7b698 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -475,7 +475,7 @@ void __iscsi_write_space_ready(struct iscsi_conn *conn) spin_lock_bh(&p->wr_lock); conn->wr_space_ready = 1; - if ((conn->wr_state == ISCSI_CONN_WR_STATE_SPACE_WAIT)) { + if (conn->wr_state == ISCSI_CONN_WR_STATE_SPACE_WAIT) { TRACE_DBG("wr space ready (conn %p)", conn); list_add_tail(&conn->wr_list_entry, &p->wr_list); conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST; diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.14.patch b/iscsi-scst/kernel/patches/put_page_callback-3.14.patch new file mode 100644 index 000000000..dcf824b17 --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.14.patch @@ -0,0 +1,364 @@ +=== modified file 'drivers/block/drbd/drbd_receiver.c' +--- old/drivers/block/drbd/drbd_receiver.c 2014-04-17 22:02:06 +0000 ++++ new/drivers/block/drbd/drbd_receiver.c 2014-04-17 22:48:38 +0000 +@@ -130,7 +130,7 @@ static int page_chain_free(struct page * + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; + +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/mm_types.h 2014-04-17 22:48:38 +0000 +@@ -195,6 +195,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/net.h 2014-04-17 22:48:38 +0000 +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -292,6 +293,45 @@ int kernel_sendpage(struct socket *sock, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/skbuff.h 2014-04-17 22:48:38 +0000 +@@ -2056,7 +2056,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -2079,7 +2079,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2014-04-17 22:02:06 +0000 ++++ new/net/Kconfig 2014-04-17 22:48:38 +0000 +@@ -75,6 +75,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/ceph/pagevec.c' +--- old/net/ceph/pagevec.c 2014-04-17 22:02:06 +0000 ++++ new/net/ceph/pagevec.c 2014-04-17 22:48:38 +0000 +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page ** + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + kfree(pages); + } + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2014-04-17 22:02:06 +0000 ++++ new/net/core/skbuff.c 2014-04-17 22:48:38 +0000 +@@ -425,7 +425,7 @@ struct sk_buff *__netdev_alloc_skb(struc + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -483,7 +483,7 @@ static void skb_clone_fraglist(struct sk + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -804,7 +804,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1647,7 +1647,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1700,7 +1700,7 @@ static bool spd_fill_page(struct splice_ + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2159,7 +2159,7 @@ skb_zerocopy(struct sk_buff *to, struct + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } +@@ -2813,7 +2813,7 @@ int skb_append_datato_frags(struct sock + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); + +=== modified file 'net/core/sock.c' +--- old/net/core/sock.c 2014-04-17 22:02:06 +0000 ++++ new/net/core/sock.c 2014-04-17 22:48:38 +0000 +@@ -1839,7 +1839,7 @@ bool skb_page_frag_refill(unsigned int s + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + order = SKB_FRAG_PAGE_ORDER; +@@ -2602,7 +2602,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2014-04-17 22:02:06 +0000 ++++ new/net/ipv4/Makefile 2014-04-17 22:48:38 +0000 +@@ -53,6 +53,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah. + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2014-04-17 22:02:06 +0000 ++++ new/net/ipv4/ip_output.c 2014-04-17 22:48:38 +0000 +@@ -1004,7 +1004,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1230,7 +1230,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2014-04-17 22:02:06 +0000 ++++ new/net/ipv4/tcp.c 2014-04-17 22:48:38 +0000 +@@ -939,7 +939,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1238,7 +1238,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2014-04-17 22:48:38 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + +=== modified file 'net/ipv6/ip6_output.c' +--- old/net/ipv6/ip6_output.c 2014-04-17 22:02:06 +0000 ++++ new/net/ipv6/ip6_output.c 2014-04-17 22:48:38 +0000 +@@ -1455,7 +1455,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, + diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch b/iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch new file mode 100644 index 000000000..6394566da --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch @@ -0,0 +1,287 @@ +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2012-01-10 22:58:17 +0000 ++++ new/include/linux/mm_types.h 2012-01-10 23:02:48 +0000 +@@ -149,6 +149,17 @@ struct page { + */ + void *shadow; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * If another subsystem starts using the double word pairing for atomic + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2012-01-10 22:58:17 +0000 ++++ new/include/linux/net.h 2012-01-10 23:02:48 +0000 +@@ -61,6 +61,7 @@ typedef enum { + #include /* For O_CLOEXEC and O_NONBLOCK */ + #include + #include ++#include + + struct poll_table_struct; + struct pipe_inode_info; +@@ -289,5 +290,44 @@ extern int kernel_sock_shutdown(struct s + MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \ + "-type-" __stringify(type)) + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #endif /* __KERNEL__ */ + #endif /* _LINUX_NET_H */ + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2012-01-10 22:58:17 +0000 ++++ new/include/linux/skbuff.h 2012-01-10 23:15:31 +0000 +@@ -1712,7 +1712,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -1735,7 +1735,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2012-01-10 22:58:17 +0000 ++++ new/net/Kconfig 2012-01-10 23:02:48 +0000 +@@ -72,6 +72,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2012-01-10 22:58:17 +0000 ++++ new/net/core/skbuff.c 2012-01-10 23:02:48 +0000 +@@ -654,7 +654,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)head->private; +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1493,7 +1493,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static inline struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1517,7 +1517,7 @@ new_page: + off = sk->sk_sndmsg_off; + mlen = PAGE_SIZE - off; + if (mlen < 64 && mlen < *len) { +- put_page(p); ++ net_put_page(p); + goto new_page; + } + +@@ -1527,7 +1527,7 @@ new_page: + memcpy(page_address(p) + off, page_address(page) + *offset, *len); + sk->sk_sndmsg_off += *len; + *offset = off; +- get_page(p); ++ net_get_page(p); + + return p; + } +@@ -1549,7 +1549,7 @@ static inline int spd_fill_page(struct s + if (!page) + return 1; + } else +- get_page(page); ++ net_get_page(page); + + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2012-01-10 22:58:17 +0000 ++++ new/net/ipv4/Makefile 2012-01-10 23:02:48 +0000 +@@ -48,6 +48,7 @@ obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o + obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2012-01-10 22:58:17 +0000 ++++ new/net/ipv4/ip_output.c 2012-01-10 23:02:48 +0000 +@@ -1232,7 +1232,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2012-01-10 22:58:17 +0000 ++++ new/net/ipv4/tcp.c 2012-01-10 23:02:48 +0000 +@@ -815,7 +815,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + +@@ -1022,7 +1022,7 @@ new_segment: + goto new_segment; + } else if (page) { + if (off == PAGE_SIZE) { +- put_page(page); ++ net_put_page(page); + TCP_PAGE(sk) = page = NULL; + off = 0; + } +@@ -1062,9 +1062,9 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, page, off, copy); + if (TCP_PAGE(sk)) { +- get_page(page); ++ net_get_page(page); + } else if (off + copy < PAGE_SIZE) { +- get_page(page); ++ net_get_page(page); + TCP_PAGE(sk) = page; + } + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2012-01-10 23:43:22 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index 15253e973..f01180f32 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,18 +3,19 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.13.9 \ -3.12.13-nc \ +3.14.1 \ +3.13.10 \ +3.12.17-nc \ 3.11.10-nc \ -3.10.36-nc \ +3.10.37-nc \ 3.9.11-nc \ -3.8.13-nc \ +3.8.14-nc \ 3.7.10-nc \ 3.6.11-nc \ 3.5.7-nc \ -3.4.86-nc \ +3.4.87-nc \ 3.3.8-nc \ -3.2.53-nc \ +3.2.57-nc \ 3.1.10-nc \ 3.0.101-nc \ 2.6.39.4-nc \ diff --git a/qla2x00t/qla2x00-target/Makefile_in-tree-3.14 b/qla2x00t/qla2x00-target/Makefile_in-tree-3.14 new file mode 100644 index 000000000..9657aee84 --- /dev/null +++ b/qla2x00t/qla2x00-target/Makefile_in-tree-3.14 @@ -0,0 +1,5 @@ +ccflags-y += -Idrivers/scsi/qla2xxx + +qla2x00tgt-y := qla2x00t.o + +obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o diff --git a/scripts/generate-kernel-patch b/scripts/generate-kernel-patch index 532572fb4..cc95d7a67 100755 --- a/scripts/generate-kernel-patch +++ b/scripts/generate-kernel-patch @@ -268,7 +268,9 @@ done scsi_exec_req_fifo_defined=0 scst_io_context=0 for p in scst/kernel/*-${kver}.patch \ - $(if [ "${1#3.7.}" != "$1" ] && [ "${1#3.7.}" -ge 10 ]; then + $(if [ "${1#3.2.}" != "$1" ] && [ "${1#3.2.}" -ge 57 ]; then + echo iscsi-scst/kernel/patches/*-3.2.57.patch; + elif [ "${1#3.7.}" != "$1" ] && [ "${1#3.7.}" -ge 10 ]; then echo iscsi-scst/kernel/patches/*-3.7.10.patch; elif [ "${1#3.10.}" != "$1" ] && [ "${1#3.10.}" -ge 30 ]; then echo iscsi-scst/kernel/patches/*-3.10.30.patch; diff --git a/scripts/run-regression-tests b/scripts/run-regression-tests index c859a4c00..bae2ad0a5 100755 --- a/scripts/run-regression-tests +++ b/scripts/run-regression-tests @@ -213,6 +213,7 @@ function run_checkpatch { echo "${errors} errors / ${warnings} warnings." grep -E '^WARNING|^ERROR' "${outputfile}" | sort | + grep -v 'WARNING: missing space after return type' | sed 's/^WARNING: Avoid CamelCase:.*/WARNING: Avoid CamelCase/' | uniq -c else diff --git a/scst/README b/scst/README index 6234643f1..2306f1371 100644 --- a/scst/README +++ b/scst/README @@ -1466,34 +1466,29 @@ Report target port groups: Initiator Support ................. -On Linux systems implicit ALUA support is provided by the scsi_dh_alua driver -of the device mapper. You will have to modify at least the following in -/etc/multipath.conf: -* path_checker scsi_dh_alua -* prio_callout "/sbin/mpath_prio_alua /dev/%n" +On Linux systems implicit ALUA support is provided by the scsi_dh_alua kernel +driver in combination with the user space multipathd daemon. You will have to +modify at least the following in /etc/multipath.conf to enable implicit ALUA: +* hardware_handler "1 alua" +* prio alua +* path_grouping_policy group_by_prio -If your distribution does not provide a /sbin/mpath_prio_alua script, you can -use the following implementation: -$ cat /sbin/mpath_prio_alua -#!/bin/bash -# Given a SCSI device node, query the target port group asymmetric access -# state and report it in numeric form. -tpg_id="$(sg_vpd --page=di "$1" | sed -n 's/.*Target port group: //p')" -aas="$(sg_rtpg "$1" \ -| grep -A1 "target port group id : $tpg_id" \ -| tail -n 1 \ -| sed 's/.*target port group asymmetric access state : //')" -echo $((aas)) +Note: newer versions of multipathd support a parameter called +"detect_prio". It can be more convenient to enable this parameter instead of +setting the parameter "prio" to "alua" for only those LUNs that support ALUA. More information about how to configure the device mapper and the scsi_dh_alua -driver can be found in the manual of your Linux distribution. +driver can be found in the manual of your Linux distribution ("man +multipath.conf"). Windows initiator systems support ALUA from Windows Server 2008 on. For more -information, see also: -* Microsoft, Multipathing Support in Windows Server 2008, MSDN -(http://blogs.msdn.com/b/san/archive/2008/07/27/multipathing-support-in-windows-server-2008.aspx). +information about ALUA support in Windows Server, see also: +* Microsoft, Windows Server 2008 R2 Multipath I/O Overview, MSDN + (http://technet.microsoft.com/en-us/library/cc725907.aspx). +* Microsoft, Multipathing Support in Windows Server 2008, July 2008, MSDN + (http://blogs.msdn.com/b/san/archive/2008/07/27/multipathing-support-in-windows-server-2008.aspx). * Microsoft, ALUA MPIO Logo Test, MSDN -(http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx). + (http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx). Caching diff --git a/scst/include/scst.h b/scst/include/scst.h index 378dc589c..518e434a5 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -661,6 +661,7 @@ struct scst_acg; struct scst_acg_dev; struct scst_acn; struct scst_aen; +struct scst_opcode_descriptor; /* * SCST uses 64-bit numbers to represent LUN's internally. The value @@ -763,7 +764,7 @@ struct scst_tgt_template { * * MUST HAVE */ - int (*xmit_response) (struct scst_cmd *cmd); + int (*xmit_response)(struct scst_cmd *cmd); /* * This function informs the driver that data @@ -787,7 +788,7 @@ struct scst_tgt_template { * * OPTIONAL */ - int (*rdy_to_xfer) (struct scst_cmd *cmd); + int (*rdy_to_xfer)(struct scst_cmd *cmd); /* * Called if cmd stays inside the target hardware, i.e. after @@ -797,7 +798,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*on_hw_pending_cmd_timeout) (struct scst_cmd *cmd); + void (*on_hw_pending_cmd_timeout)(struct scst_cmd *cmd); /* * Called to notify the driver that the command is about to be freed. @@ -813,7 +814,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*on_free_cmd) (struct scst_cmd *cmd); + void (*on_free_cmd)(struct scst_cmd *cmd); /* * This function allows target driver to handle data buffer @@ -844,7 +845,7 @@ struct scst_tgt_template { * * OPTIONAL. */ - int (*tgt_alloc_data_buf) (struct scst_cmd *cmd); + int (*tgt_alloc_data_buf)(struct scst_cmd *cmd); /* * This function informs the driver that data @@ -871,7 +872,7 @@ struct scst_tgt_template { * * OPTIONAL. */ - void (*preprocessing_done) (struct scst_cmd *cmd); + void (*preprocessing_done)(struct scst_cmd *cmd); /* * This function informs the driver that the said command is about @@ -884,7 +885,7 @@ struct scst_tgt_template { * * OPTIONAL */ - int (*pre_exec) (struct scst_cmd *cmd); + int (*pre_exec)(struct scst_cmd *cmd); /* * This function informs the driver that all affected by the @@ -897,7 +898,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*task_mgmt_affected_cmds_done) (struct scst_mgmt_cmd *mgmt_cmd); + void (*task_mgmt_affected_cmds_done)(struct scst_mgmt_cmd *mgmt_cmd); /* * This function informs the driver that the corresponding task @@ -910,7 +911,7 @@ struct scst_tgt_template { * * MUST HAVE if the target supports task management. */ - void (*task_mgmt_fn_done) (struct scst_mgmt_cmd *mgmt_cmd); + void (*task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd); /* * Called to notify target driver that the command is being aborted. @@ -919,7 +920,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*on_abort_cmd) (struct scst_cmd *cmd); + void (*on_abort_cmd)(struct scst_cmd *cmd); /* * This function should detect the target adapters that @@ -930,7 +931,7 @@ struct scst_tgt_template { * * MUST HAVE */ - int (*detect) (struct scst_tgt_template *tgt_template); + int (*detect)(struct scst_tgt_template *tgt_template); /* * This function should free up the resources allocated to the device. @@ -940,7 +941,7 @@ struct scst_tgt_template { * * MUST HAVE */ - int (*release) (struct scst_tgt *tgt); + int (*release)(struct scst_tgt *tgt); /* * This function is used for Asynchronous Event Notifications. @@ -959,7 +960,7 @@ struct scst_tgt_template { * * MUST HAVE, if low-level protocol supports AENs. */ - int (*report_aen) (struct scst_aen *aen); + int (*report_aen)(struct scst_aen *aen); #ifdef CONFIG_SCST_PROC /* @@ -969,8 +970,8 @@ struct scst_tgt_template { * * OPTIONAL */ - int (*read_proc) (struct seq_file *seq, struct scst_tgt *tgt); - int (*write_proc) (char *buffer, char **start, off_t offset, + int (*read_proc)(struct seq_file *seq, struct scst_tgt *tgt); + int (*write_proc)(char *buffer, char **start, off_t offset, int length, int *eof, struct scst_tgt *tgt); #endif @@ -988,7 +989,7 @@ struct scst_tgt_template { * * SHOULD HAVE, because it's required for Persistent Reservations. */ - int (*get_initiator_port_transport_id) (struct scst_tgt *tgt, + int (*get_initiator_port_transport_id)(struct scst_tgt *tgt, struct scst_session *sess, uint8_t **transport_id); /* @@ -1003,14 +1004,14 @@ struct scst_tgt_template { * If you are sure your target driver doesn't need enabling target, * you should set enabled_attr_not_needed in 1. */ - int (*enable_target) (struct scst_tgt *tgt, bool enable); + int (*enable_target)(struct scst_tgt *tgt, bool enable); /* * This function shows if particular target is enabled or not. * * SHOULD HAVE, see above why. */ - bool (*is_target_enabled) (struct scst_tgt *tgt); + bool (*is_target_enabled)(struct scst_tgt *tgt); /* * This function adds a virtual target. @@ -1030,7 +1031,7 @@ struct scst_tgt_template { * * MUST HAVE if virtual targets are supported. */ - ssize_t (*add_target) (const char *target_name, char *params); + ssize_t (*add_target)(const char *target_name, char *params); /* * This function deletes a virtual target. See comment for add_target @@ -1038,7 +1039,7 @@ struct scst_tgt_template { * * MUST HAVE if virtual targets are supported. */ - ssize_t (*del_target) (const char *target_name); + ssize_t (*del_target)(const char *target_name); /* * This function called if not "add_target" or "del_target" command is @@ -1047,7 +1048,7 @@ struct scst_tgt_template { * * OPTIONAL. */ - ssize_t (*mgmt_cmd) (char *cmd); + ssize_t (*mgmt_cmd)(char *cmd); /* * Forcibly close a session. Note: this function may operate @@ -1068,7 +1069,7 @@ struct scst_tgt_template { * * OPTIONAL */ - uint16_t (*get_phys_transport_version) (struct scst_tgt *tgt); + uint16_t (*get_phys_transport_version)(struct scst_tgt *tgt); /* * Should return SCSI transport version. Used in the corresponding @@ -1076,7 +1077,7 @@ struct scst_tgt_template { * * OPTIONAL */ - uint16_t (*get_scsi_transport_version) (struct scst_tgt *tgt); + uint16_t (*get_scsi_transport_version)(struct scst_tgt *tgt); /* * Name of the template. Must be unique to identify @@ -1244,7 +1245,7 @@ struct scst_dev_type { * * MUST HAVE */ - int (*parse) (struct scst_cmd *cmd); + int (*parse)(struct scst_cmd *cmd); /* * This function allows dev handler to handle data buffer @@ -1264,7 +1265,7 @@ struct scst_dev_type { * * OPTIONAL */ - int (*dev_alloc_data_buf) (struct scst_cmd *cmd); + int (*dev_alloc_data_buf)(struct scst_cmd *cmd); /* * Called to execute CDB. Useful, for instance, to implement @@ -1286,7 +1287,7 @@ struct scst_dev_type { * OPTIONAL, if not set, the commands will be sent directly to SCSI * device. */ - int (*exec) (struct scst_cmd *cmd); + int (*exec)(struct scst_cmd *cmd); /* * Called to notify dev handler about the result of cmd execution @@ -1305,7 +1306,7 @@ struct scst_dev_type { * * OPTIONAL */ - int (*dev_done) (struct scst_cmd *cmd); + int (*dev_done)(struct scst_cmd *cmd); /* * Called to notify dev hander that the command is about to be freed. @@ -1314,7 +1315,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*on_free_cmd) (struct scst_cmd *cmd); + void (*on_free_cmd)(struct scst_cmd *cmd); /* * Called to notify dev handler that a task management command received @@ -1329,7 +1330,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*task_mgmt_fn_received) (struct scst_mgmt_cmd *mgmt_cmd, + void (*task_mgmt_fn_received)(struct scst_mgmt_cmd *mgmt_cmd, struct scst_tgt_dev *tgt_dev); /* @@ -1347,7 +1348,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*task_mgmt_fn_done) (struct scst_mgmt_cmd *mgmt_cmd, + void (*task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd, struct scst_tgt_dev *tgt_dev); /* @@ -1359,7 +1360,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*reassign_retained_states) (struct scst_tgt_dev *new_tgt_dev, + void (*reassign_retained_states)(struct scst_tgt_dev *new_tgt_dev, struct scst_tgt_dev *old_tgt_dev); /* @@ -1372,7 +1373,30 @@ struct scst_dev_type { * * MUST HAVE, if dev handler supports CDB splitting. */ - bool (*on_sg_tablesize_low) (struct scst_cmd *cmd); + bool (*on_sg_tablesize_low)(struct scst_cmd *cmd); + + /* + * Called to return array of supported opcodes in out_supp_opcodes + * argument with out_supp_opcodes_cnt elements count or execute + * REPORT SUPPORTED OPERATION CODES command in place. Must return + * 0 on success or any other code otherwise. In the latter case, + * cmd supposed to have correct sense set. + * + * OPTIONAL + */ + int (*get_supported_opcodes)(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); + + /* + * Called to put (release) array of supported opcodes returned + * by get_supported_opcodes() callback. + * + * OPTIONAL + */ + void (*put_supported_opcodes)(struct scst_cmd *cmd, + const struct scst_opcode_descriptor **supp_opcodes, + int supp_opcodes_cnt); /* * Called when new device is attaching to the dev handler @@ -1380,14 +1404,14 @@ struct scst_dev_type { * * OPTIONAL */ - int (*attach) (struct scst_device *dev); + int (*attach)(struct scst_device *dev); /* * Called when a device is detaching from the dev handler. * * OPTIONAL */ - void (*detach) (struct scst_device *dev); + void (*detach)(struct scst_device *dev); /* * Called when new tgt_dev (session) is attaching to the dev handler. @@ -1395,14 +1419,14 @@ struct scst_dev_type { * * OPTIONAL */ - int (*attach_tgt) (struct scst_tgt_dev *tgt_dev); + int (*attach_tgt)(struct scst_tgt_dev *tgt_dev); /* * Called when tgt_dev (session) is detaching from the dev handler. * * OPTIONAL */ - void (*detach_tgt) (struct scst_tgt_dev *tgt_dev); + void (*detach_tgt)(struct scst_tgt_dev *tgt_dev); #ifdef CONFIG_SCST_PROC /* @@ -1412,8 +1436,8 @@ struct scst_dev_type { * * OPTIONAL */ - int (*read_proc) (struct seq_file *seq, struct scst_dev_type *dev_type); - int (*write_proc) (char *buffer, char **start, off_t offset, + int (*read_proc)(struct seq_file *seq, struct scst_dev_type *dev_type); + int (*write_proc)(char *buffer, char **start, off_t offset, int length, int *eof, struct scst_dev_type *dev_type); #else /* @@ -1434,7 +1458,7 @@ struct scst_dev_type { * * MUST HAVE if virtual devices are supported. */ - ssize_t (*add_device) (const char *device_name, char *params); + ssize_t (*add_device)(const char *device_name, char *params); /* * This function deletes a virtual device. See comment for add_device @@ -1442,7 +1466,7 @@ struct scst_dev_type { * * MUST HAVE if virtual devices are supported. */ - ssize_t (*del_device) (const char *device_name); + ssize_t (*del_device)(const char *device_name); /* * This function called if not "add_device" or "del_device" command is @@ -1451,7 +1475,7 @@ struct scst_dev_type { * * OPTIONAL. */ - ssize_t (*mgmt_cmd) (char *cmd); + ssize_t (*mgmt_cmd)(char *cmd); #endif /* @@ -1778,9 +1802,9 @@ struct scst_session { * and scst_unregister_session() */ void *reg_sess_data; - void (*init_result_fn) (struct scst_session *sess, void *data, + void (*init_result_fn)(struct scst_session *sess, void *data, int result); - void (*unreg_done_fn) (struct scst_session *sess); + void (*unreg_done_fn)(struct scst_session *sess); #ifdef CONFIG_SCST_MEASURE_LATENCY spinlock_t lat_lock; @@ -1803,7 +1827,7 @@ struct scst_pr_abort_all_pending_mgmt_cmds_counter { atomic_t pr_abort_pending_cnt; /* Saved completion routine */ - void (*saved_cmd_done) (struct scst_cmd *cmd, int next_state, + void (*saved_cmd_done)(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context); /* @@ -2111,7 +2135,7 @@ struct scst_cmd { int64_t data_len; /* Completion routine */ - void (*scst_cmd_done) (struct scst_cmd *cmd, int next_state, + void (*scst_cmd_done)(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context); struct sgv_pool_obj *sgv; /* sgv object */ @@ -2896,6 +2920,58 @@ struct scst_aen { int delivery_status; }; +#define SCST_OD_DEFAULT_CONTROL_BYTE 0 + +struct scst_opcode_descriptor { + uint16_t od_serv_action; + uint8_t od_opcode; + uint8_t od_serv_action_valid:1; + uint8_t od_support:3; /* SUPPORT bits */ + uint16_t od_cdb_size; + uint8_t od_comm_specific_timeout; + uint32_t od_nominal_timeout; + uint32_t od_recommended_timeout; + uint8_t od_cdb_usage_bits[]; +} __packed; + +extern const struct scst_opcode_descriptor scst_op_descr_log_select; +extern const struct scst_opcode_descriptor scst_op_descr_log_sense; +extern const struct scst_opcode_descriptor scst_op_descr_mode_select6; +extern const struct scst_opcode_descriptor scst_op_descr_mode_sense6; +extern const struct scst_opcode_descriptor scst_op_descr_mode_select10; +extern const struct scst_opcode_descriptor scst_op_descr_mode_sense10; +extern const struct scst_opcode_descriptor scst_op_descr_rtpg; +extern const struct scst_opcode_descriptor scst_op_descr_stpg; +extern const struct scst_opcode_descriptor scst_op_descr_send_diagnostic; + +extern const struct scst_opcode_descriptor scst_op_descr_inquiry; +extern const struct scst_opcode_descriptor scst_op_descr_tur; +extern const struct scst_opcode_descriptor scst_op_descr_reserve6; +extern const struct scst_opcode_descriptor scst_op_descr_release6; +extern const struct scst_opcode_descriptor scst_op_descr_reserve10; +extern const struct scst_opcode_descriptor scst_op_descr_release10; +extern const struct scst_opcode_descriptor scst_op_descr_pr_in; +extern const struct scst_opcode_descriptor scst_op_descr_pr_out; +extern const struct scst_opcode_descriptor scst_op_descr_report_luns; +extern const struct scst_opcode_descriptor scst_op_descr_request_sense; +extern const struct scst_opcode_descriptor scst_op_descr_report_supp_tm_fns; +extern const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes; + +#define SCST_OPCODE_DESCRIPTORS \ + &scst_op_descr_inquiry, \ + &scst_op_descr_tur, \ + &scst_op_descr_reserve6, \ + &scst_op_descr_release6, \ + &scst_op_descr_reserve10, \ + &scst_op_descr_release10, \ + &scst_op_descr_pr_in, \ + &scst_op_descr_pr_out, \ + &scst_op_descr_report_luns, \ + &scst_op_descr_request_sense, \ + &scst_op_descr_report_supp_opcodes, \ + &scst_op_descr_report_supp_tm_fns, + + #ifndef smp_mb__after_set_bit /* There is no smp_mb__after_set_bit() in the kernel */ #define smp_mb__after_set_bit() smp_mb() @@ -2935,11 +3011,11 @@ void scst_unregister_target(struct scst_tgt *tgt); struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic, const char *initiator_name, void *tgt_priv, void *result_fn_data, - void (*result_fn) (struct scst_session *sess, void *data, int result)); + void (*result_fn)(struct scst_session *sess, void *data, int result)); struct scst_session *scst_register_session_non_gpl(struct scst_tgt *tgt, const char *initiator_name, void *tgt_priv); void scst_unregister_session(struct scst_session *sess, int wait, - void (*unreg_done_fn) (struct scst_session *sess)); + void (*unreg_done_fn)(struct scst_session *sess)); void scst_unregister_session_non_gpl(struct scst_session *sess); int __scst_register_dev_driver(struct scst_dev_type *dev_type, @@ -3078,8 +3154,8 @@ void scst_capacity_data_changed(struct scst_device *dev); struct scst_cmd *scst_find_cmd_by_tag(struct scst_session *sess, uint64_t tag); struct scst_cmd *scst_find_cmd(struct scst_session *sess, void *data, - int (*cmp_fn) (struct scst_cmd *cmd, - void *data)); + int (*cmp_fn)(struct scst_cmd *cmd, + void *data)); enum dma_data_direction scst_to_dma_dir(int scst_dir); enum dma_data_direction scst_to_tgt_dma_dir(int scst_dir); diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index 06bf7e923..e4ebb6944 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -413,6 +413,10 @@ static inline int scst_sense_response_code(const uint8_t *sense) #define WRITE_SAME_16 0x93 #endif +#ifndef SYNCHRONIZE_CACHE_16 +#define SYNCHRONIZE_CACHE_16 0x91 +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) /* * From . See also commit @@ -567,6 +571,7 @@ enum scst_tg_sup { ** Various timeouts *************************************************************/ #define SCST_DEFAULT_TIMEOUT (30 * HZ) +#define SCST_DEFAULT_NOMINAL_TIMEOUT_SEC 1 #define SCST_GENERIC_CHANGER_TIMEOUT (3 * HZ) #define SCST_GENERIC_CHANGER_LONG_TIMEOUT (14000 * HZ) @@ -582,8 +587,8 @@ enum scst_tg_sup { #define SCST_GENERIC_MODISK_REG_TIMEOUT (900 * HZ) #define SCST_GENERIC_MODISK_LONG_TIMEOUT (14000 * HZ) -#define SCST_GENERIC_DISK_SMALL_TIMEOUT (3 * HZ) -#define SCST_GENERIC_DISK_REG_TIMEOUT (30 * HZ) +#define SCST_GENERIC_DISK_SMALL_TIMEOUT (10 * HZ) +#define SCST_GENERIC_DISK_REG_TIMEOUT (60 * HZ) #define SCST_GENERIC_DISK_LONG_TIMEOUT (3600 * HZ) #define SCST_GENERIC_RAID_TIMEOUT (3 * HZ) diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch new file mode 100644 index 000000000..0d5a19f0f --- /dev/null +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/Kconfig b/drivers/Kconfig +index aa43b91..c96860e 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig" + + source "drivers/scsi/Kconfig" + ++source "drivers/scst/Kconfig" ++ + source "drivers/ata/Kconfig" + + source "drivers/md/Kconfig" diff --git a/scst/kernel/in-tree/Makefile.dev_handlers-3.14 b/scst/kernel/in-tree/Makefile.dev_handlers-3.14 new file mode 100644 index 000000000..f933b36f7 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.dev_handlers-3.14 @@ -0,0 +1,14 @@ +ccflags-y += -Wno-unused-parameter + +obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \ + scst_vdisk.o scst_raid.o scst_processor.o scst_user.o + +obj-$(CONFIG_SCST_DISK) += scst_disk.o +obj-$(CONFIG_SCST_TAPE) += scst_tape.o +obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o +obj-$(CONFIG_SCST_MODISK) += scst_modisk.o +obj-$(CONFIG_SCST_CHANGER) += scst_changer.o +obj-$(CONFIG_SCST_RAID) += scst_raid.o +obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o +obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o +obj-$(CONFIG_SCST_USER) += scst_user.o diff --git a/scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch new file mode 100644 index 000000000..f7213ed4c --- /dev/null +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/Makefile b/drivers/Makefile +index ab93de8..45077ec 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -128,6 +128,7 @@ obj-$(CONFIG_SSB) += ssb/ + obj-$(CONFIG_BCMA) += bcma/ + obj-$(CONFIG_VHOST_RING) += vhost/ + obj-$(CONFIG_VLYNQ) += vlynq/ ++obj-$(CONFIG_SCST) += scst/ + obj-$(CONFIG_STAGING) += staging/ + obj-y += platform/ + #common clk code diff --git a/scst/kernel/in-tree/Makefile.scst-3.14 b/scst/kernel/in-tree/Makefile.scst-3.14 new file mode 100644 index 000000000..53af5f388 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.scst-3.14 @@ -0,0 +1,13 @@ +ccflags-y += -Wno-unused-parameter + +scst-y += scst_main.o +scst-y += scst_pres.o +scst-y += scst_targ.o +scst-y += scst_lib.o +scst-y += scst_sysfs.o +scst-y += scst_mem.o +scst-y += scst_tg.o +scst-y += scst_debug.o + +obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \ + srpt/ scst_local/ diff --git a/scst/kernel/scst_exec_req_fifo-3.14.patch b/scst/kernel/scst_exec_req_fifo-3.14.patch new file mode 100644 index 000000000..70c47797d --- /dev/null +++ b/scst/kernel/scst_exec_req_fifo-3.14.patch @@ -0,0 +1,528 @@ +=== modified file 'block/blk-map.c' +--- old/block/blk-map.c 2014-04-17 22:02:06 +0000 ++++ new/block/blk-map.c 2014-04-17 22:08:48 +0000 +@@ -5,6 +5,8 @@ + #include + #include + #include ++#include ++#include + #include /* for struct sg_iovec */ + + #include "blk.h" +@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio) + } + EXPORT_SYMBOL(blk_rq_unmap_user); + ++struct blk_kern_sg_work { ++ atomic_t bios_inflight; ++ struct sg_table sg_table; ++ struct scatterlist *src_sgl; ++}; ++ ++static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw) ++{ ++ struct sg_table *sgt = &bw->sg_table; ++ struct scatterlist *sg; ++ int i; ++ ++ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { ++ struct page *pg = sg_page(sg); ++ if (pg == NULL) ++ break; ++ __free_page(pg); ++ } ++ ++ sg_free_table(sgt); ++ kfree(bw); ++ return; ++} ++ ++static void blk_bio_map_kern_endio(struct bio *bio, int err) ++{ ++ struct blk_kern_sg_work *bw = bio->bi_private; ++ ++ if (bw != NULL) { ++ /* Decrement the bios in processing and, if zero, free */ ++ BUG_ON(atomic_read(&bw->bios_inflight) <= 0); ++ if (atomic_dec_and_test(&bw->bios_inflight)) { ++ if ((bio_data_dir(bio) == READ) && (err == 0)) { ++ unsigned long flags; ++ ++ local_irq_save(flags); /* to protect KMs */ ++ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0); ++ local_irq_restore(flags); ++ } ++ blk_free_kern_sg_work(bw); ++ } ++ } ++ ++ bio_put(bio); ++ return; ++} ++ ++static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work **pbw, ++ gfp_t gfp, gfp_t page_gfp) ++{ ++ int res = 0, i; ++ struct scatterlist *sg; ++ struct scatterlist *new_sgl; ++ int new_sgl_nents; ++ size_t len = 0, to_copy; ++ struct blk_kern_sg_work *bw; ++ ++ bw = kzalloc(sizeof(*bw), gfp); ++ if (bw == NULL) ++ goto out; ++ ++ bw->src_sgl = sgl; ++ ++ for_each_sg(sgl, sg, nents, i) ++ len += sg->length; ++ to_copy = len; ++ ++ new_sgl_nents = PFN_UP(len); ++ ++ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp); ++ if (res != 0) ++ goto err_free; ++ ++ new_sgl = bw->sg_table.sgl; ++ ++ for_each_sg(new_sgl, sg, new_sgl_nents, i) { ++ struct page *pg; ++ ++ pg = alloc_page(page_gfp); ++ if (pg == NULL) ++ goto err_free; ++ ++ sg_assign_page(sg, pg); ++ sg->length = min_t(size_t, PAGE_SIZE, len); ++ ++ len -= PAGE_SIZE; ++ } ++ ++ if (rq_data_dir(rq) == WRITE) { ++ /* ++ * We need to limit amount of copied data to to_copy, because ++ * sgl might have the last element in sgl not marked as last in ++ * SG chaining. ++ */ ++ sg_copy(new_sgl, sgl, 0, to_copy); ++ } ++ ++ *pbw = bw; ++ /* ++ * REQ_COPY_USER name is misleading. It should be something like ++ * REQ_HAS_TAIL_SPACE_FOR_PADDING. ++ */ ++ rq->cmd_flags |= REQ_COPY_USER; ++ ++out: ++ return res; ++ ++err_free: ++ blk_free_kern_sg_work(bw); ++ res = -ENOMEM; ++ goto out; ++} ++ ++static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work *bw, gfp_t gfp) ++{ ++ int res; ++ struct request_queue *q = rq->q; ++ int rw = rq_data_dir(rq); ++ int max_nr_vecs, i; ++ size_t tot_len; ++ bool need_new_bio; ++ struct scatterlist *sg, *prev_sg = NULL; ++ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL; ++ int bios; ++ ++ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) { ++ WARN_ON(1); ++ res = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Let's keep each bio allocation inside a single page to decrease ++ * probability of failure. ++ */ ++ max_nr_vecs = min_t(size_t, ++ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)), ++ BIO_MAX_PAGES); ++ ++ need_new_bio = true; ++ tot_len = 0; ++ bios = 0; ++ for_each_sg(sgl, sg, nents, i) { ++ struct page *page = sg_page(sg); ++ void *page_addr = page_address(page); ++ size_t len = sg->length, l; ++ size_t offset = sg->offset; ++ ++ tot_len += len; ++ prev_sg = sg; ++ ++ /* ++ * Each segment must be aligned on DMA boundary and ++ * not on stack. The last one may have unaligned ++ * length as long as the total length is aligned to ++ * DMA padding alignment. ++ */ ++ if (i == nents - 1) ++ l = 0; ++ else ++ l = len; ++ if (((sg->offset | l) & queue_dma_alignment(q)) || ++ (page_addr && object_is_on_stack(page_addr + sg->offset))) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ while (len > 0) { ++ size_t bytes; ++ int rc; ++ ++ if (need_new_bio) { ++ bio = bio_kmalloc(gfp, max_nr_vecs); ++ if (bio == NULL) { ++ res = -ENOMEM; ++ goto out_free_bios; ++ } ++ ++ if (rw == WRITE) ++ bio->bi_rw |= REQ_WRITE; ++ ++ bios++; ++ bio->bi_private = bw; ++ bio->bi_end_io = blk_bio_map_kern_endio; ++ ++ if (hbio == NULL) ++ hbio = tbio = bio; ++ else ++ tbio = tbio->bi_next = bio; ++ } ++ ++ bytes = min_t(size_t, len, PAGE_SIZE - offset); ++ ++ rc = bio_add_pc_page(q, bio, page, bytes, offset); ++ if (rc < bytes) { ++ if (unlikely(need_new_bio || (rc < 0))) { ++ if (rc < 0) ++ res = rc; ++ else ++ res = -EIO; ++ goto out_free_bios; ++ } else { ++ need_new_bio = true; ++ len -= rc; ++ offset += rc; ++ continue; ++ } ++ } ++ ++ need_new_bio = false; ++ offset = 0; ++ len -= bytes; ++ page = nth_page(page, 1); ++ } ++ } ++ ++ if (hbio == NULL) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ /* Total length must be aligned on DMA padding alignment */ ++ if ((tot_len & q->dma_pad_mask) && ++ !(rq->cmd_flags & REQ_COPY_USER)) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ if (bw != NULL) ++ atomic_set(&bw->bios_inflight, bios); ++ ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio->bi_next = NULL; ++ ++ blk_queue_bounce(q, &bio); ++ ++ res = blk_rq_append_bio(q, rq, bio); ++ if (unlikely(res != 0)) { ++ bio->bi_next = hbio; ++ hbio = bio; ++ /* We can have one or more bios bounced */ ++ goto out_unmap_bios; ++ } ++ } ++ ++ res = 0; ++ ++ rq->buffer = NULL; ++out: ++ return res; ++ ++out_unmap_bios: ++ blk_rq_unmap_kern_sg(rq, res); ++ ++out_free_bios: ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio_put(bio); ++ } ++ goto out; ++} ++ ++/** ++ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC ++ * @rq: request to fill ++ * @sgl: area to map ++ * @nents: number of elements in @sgl ++ * @gfp: memory allocation flags ++ * ++ * Description: ++ * Data will be mapped directly if possible. Otherwise a bounce ++ * buffer will be used. ++ */ ++int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp) ++{ ++ int res; ++ ++ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp); ++ if (unlikely(res != 0)) { ++ struct blk_kern_sg_work *bw = NULL; ++ ++ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw, ++ gfp, rq->q->bounce_gfp | gfp); ++ if (unlikely(res != 0)) ++ goto out; ++ ++ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl, ++ bw->sg_table.nents, bw, gfp); ++ if (res != 0) { ++ blk_free_kern_sg_work(bw); ++ goto out; ++ } ++ } ++ ++ rq->buffer = NULL; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(blk_rq_map_kern_sg); ++ ++/** ++ * blk_rq_unmap_kern_sg - unmap a request with kernel sg ++ * @rq: request to unmap ++ * @err: non-zero error code ++ * ++ * Description: ++ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called ++ * only in case of an error! ++ */ ++void blk_rq_unmap_kern_sg(struct request *rq, int err) ++{ ++ struct bio *bio = rq->bio; ++ ++ while (bio) { ++ struct bio *b = bio; ++ bio = bio->bi_next; ++ b->bi_end_io(b, err); ++ } ++ rq->bio = NULL; ++ ++ return; ++} ++EXPORT_SYMBOL(blk_rq_unmap_kern_sg); ++ + /** + * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage + * @q: request queue where request should be inserted + +=== modified file 'include/linux/blkdev.h' +--- old/include/linux/blkdev.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/blkdev.h 2014-04-17 22:08:48 +0000 +@@ -705,6 +705,8 @@ extern unsigned long blk_max_low_pfn, bl + #define BLK_DEFAULT_SG_TIMEOUT (60 * HZ) + #define BLK_MIN_SG_TIMEOUT (7 * HZ) + ++#define SCSI_EXEC_REQ_FIFO_DEFINED ++ + #ifdef CONFIG_BOUNCE + extern int init_emergency_isa_pool(void); + extern void blk_queue_bounce(struct request_queue *q, struct bio **bio); +@@ -825,6 +827,9 @@ extern int blk_rq_map_kern(struct reques + extern int blk_rq_map_user_iov(struct request_queue *, struct request *, + struct rq_map_data *, struct sg_iovec *, int, + unsigned int, gfp_t); ++extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp); ++extern void blk_rq_unmap_kern_sg(struct request *rq, int err); + extern int blk_execute_rq(struct request_queue *, struct gendisk *, + struct request *, int); + extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, + +=== modified file 'include/linux/scatterlist.h' +--- old/include/linux/scatterlist.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/scatterlist.h 2014-04-17 22:08:48 +0000 +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + struct sg_table { + struct scatterlist *sgl; /* the list */ +@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt + size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, + void *buf, size_t buflen, off_t skip); + ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len); ++ + /* + * Maximum number of entries that will be allocated in one piece, if + * a list larger than this is required then chaining will be utilized. + +=== modified file 'lib/scatterlist.c' +--- old/lib/scatterlist.c 2014-04-17 22:02:06 +0000 ++++ new/lib/scatterlist.c 2014-04-17 22:08:48 +0000 +@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter + return sg_copy_buffer(sgl, nents, buf, buflen, skip, true); + } + EXPORT_SYMBOL(sg_pcopy_to_buffer); ++ ++ ++/* ++ * Can switch to the next dst_sg element, so, to copy to strictly only ++ * one dst_sg element, it must be either last in the chain, or ++ * copy_len == dst_sg->length. ++ */ ++static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len, ++ size_t *pdst_offs, struct scatterlist *src_sg, ++ size_t copy_len) ++{ ++ int res = 0; ++ struct scatterlist *dst_sg; ++ size_t src_len, dst_len, src_offs, dst_offs; ++ struct page *src_page, *dst_page; ++ ++ dst_sg = *pdst_sg; ++ dst_len = *pdst_len; ++ dst_offs = *pdst_offs; ++ dst_page = sg_page(dst_sg); ++ ++ src_page = sg_page(src_sg); ++ src_len = src_sg->length; ++ src_offs = src_sg->offset; ++ ++ do { ++ void *saddr, *daddr; ++ size_t n; ++ ++ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) + ++ (src_offs & ~PAGE_MASK); ++ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) + ++ (dst_offs & ~PAGE_MASK); ++ ++ if (((src_offs & ~PAGE_MASK) == 0) && ++ ((dst_offs & ~PAGE_MASK) == 0) && ++ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) && ++ (copy_len >= PAGE_SIZE)) { ++ copy_page(daddr, saddr); ++ n = PAGE_SIZE; ++ } else { ++ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK), ++ PAGE_SIZE - (src_offs & ~PAGE_MASK)); ++ n = min(n, src_len); ++ n = min(n, dst_len); ++ n = min_t(size_t, n, copy_len); ++ memcpy(daddr, saddr, n); ++ } ++ dst_offs += n; ++ src_offs += n; ++ ++ kunmap_atomic(saddr); ++ kunmap_atomic(daddr); ++ ++ res += n; ++ copy_len -= n; ++ if (copy_len == 0) ++ goto out; ++ ++ src_len -= n; ++ dst_len -= n; ++ if (dst_len == 0) { ++ dst_sg = sg_next(dst_sg); ++ if (dst_sg == NULL) ++ goto out; ++ dst_page = sg_page(dst_sg); ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ } ++ } while (src_len > 0); ++ ++out: ++ *pdst_sg = dst_sg; ++ *pdst_len = dst_len; ++ *pdst_offs = dst_offs; ++ return res; ++} ++ ++/** ++ * sg_copy - copy one SG vector to another ++ * @dst_sg: destination SG ++ * @src_sg: source SG ++ * @nents_to_copy: maximum number of entries to copy ++ * @copy_len: maximum amount of data to copy. If 0, then copy all. ++ * ++ * Description: ++ * Data from the source SG vector will be copied to the destination SG ++ * vector. End of the vectors will be determined by sg_next() returning ++ * NULL. Returns number of bytes copied. ++ */ ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len) ++{ ++ int res = 0; ++ size_t dst_len, dst_offs; ++ ++ if (copy_len == 0) ++ copy_len = 0x7FFFFFFF; /* copy all */ ++ ++ if (nents_to_copy == 0) ++ nents_to_copy = 0x7FFFFFFF; /* copy all */ ++ ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ ++ do { ++ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs, ++ src_sg, copy_len); ++ copy_len -= copied; ++ res += copied; ++ if ((copy_len == 0) || (dst_sg == NULL)) ++ goto out; ++ ++ nents_to_copy--; ++ if (nents_to_copy == 0) ++ goto out; ++ ++ src_sg = sg_next(src_sg); ++ } while (src_sg != NULL); ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(sg_copy); + diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index c00798e75..58ae8a9f9 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -244,6 +244,12 @@ static int vdisk_attach(struct scst_device *dev); static void vdisk_detach(struct scst_device *dev); static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev); static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev); +static int vdisk_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); +static int vcdrom_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); static int fileio_alloc_data_buf(struct scst_cmd *cmd); static int vdisk_parse(struct scst_cmd *); static int vcdrom_parse(struct scst_cmd *); @@ -265,6 +271,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p); static enum compl_status_e blockio_exec_write_verify(struct vdisk_cmd_params *p); static enum compl_status_e fileio_exec_write_verify(struct vdisk_cmd_params *p); static enum compl_status_e nullio_exec_write_verify(struct vdisk_cmd_params *p); +static enum compl_status_e nullio_exec_verify(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_read_capacity(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p); @@ -564,6 +571,7 @@ static struct scst_dev_type vdisk_file_devtype = { .exec = vdisk_exec, .on_free_cmd = fileio_on_free_cmd, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vdisk_get_supported_opcodes, .devt_priv = (void *)fileio_ops, #ifdef CONFIG_SCST_PROC .read_proc = vdisk_read_proc, @@ -612,6 +620,7 @@ static struct scst_dev_type vdisk_blk_devtype = { .parse = non_fileio_parse, .exec = non_fileio_exec, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vdisk_get_supported_opcodes, .devt_priv = (void *)blockio_ops, #ifndef CONFIG_SCST_PROC .add_device = vdisk_add_blockio_device, @@ -654,6 +663,7 @@ static struct scst_dev_type vdisk_null_devtype = { .exec = non_fileio_exec, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, .devt_priv = (void *)nullio_ops, + .get_supported_opcodes = vdisk_get_supported_opcodes, #ifndef CONFIG_SCST_PROC .add_device = vdisk_add_nullio_device, .del_device = vdisk_del_device, @@ -693,6 +703,7 @@ static struct scst_dev_type vcdrom_devtype = { .exec = vcdrom_exec, .on_free_cmd = fileio_on_free_cmd, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vcdrom_get_supported_opcodes, #ifdef CONFIG_SCST_PROC .read_proc = vcdrom_read_proc, .write_proc = vcdrom_write_proc, @@ -1397,8 +1408,314 @@ static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) return INVALID_OPCODE; } +#define VDEV_DEF_RDPROTECT 0 +#define VDEV_DEF_WRPROTECT 0 +#define VDEV_DEF_VRPROTECT 0 + +#define VDEF_DEF_GROUP_NUM 0 + +static const struct scst_opcode_descriptor scst_op_descr_cwr = { + .od_opcode = COMPARE_AND_WRITE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { COMPARE_AND_WRITE, VDEV_DEF_WRPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 0, 0, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_format_unit = { + .od_opcode = FORMAT_UNIT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_LONG_TIMEOUT/HZ, + .od_cdb_usage_bits = { FORMAT_UNIT, 0xF0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_get_lba_status = { + .od_opcode = SERVICE_ACTION_IN, + .od_serv_action = SAI_GET_LBA_STATUS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SERVICE_ACTION_IN, SAI_GET_LBA_STATUS, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_allow_medium_removal = { + .od_opcode = ALLOW_MEDIUM_REMOVAL, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 3, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read6 = { + .od_opcode = READ_6, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_6, 0x1F, + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read10 = { + .od_opcode = READ_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_10, VDEV_DEF_RDPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read12 = { + .od_opcode = READ_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_12, VDEV_DEF_RDPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read16 = { + .od_opcode = READ_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_16, VDEV_DEF_RDPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_capacity = { + .od_opcode = READ_CAPACITY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, + 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_capacity16 = { + .od_opcode = SERVICE_ACTION_IN, + .od_serv_action = SAI_READ_CAPACITY_16, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SERVICE_ACTION_IN, SAI_READ_CAPACITY_16, + 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_start_stop_unit = { + .od_opcode = START_STOP, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { START_STOP, 1, 0, 0xF, 0xF7, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_sync_cache10 = { + .od_opcode = SYNCHRONIZE_CACHE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { SYNCHRONIZE_CACHE, 2, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_sync_cache16 = { + .od_opcode = SYNCHRONIZE_CACHE_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { SYNCHRONIZE_CACHE_16, 2, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_unmap = { + .od_opcode = UNMAP, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { UNMAP, 0, 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify10 = { + .od_opcode = VERIFY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY, VDEV_DEF_VRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify12 = { + .od_opcode = VERIFY_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY_12, VDEV_DEF_VRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify16 = { + .od_opcode = VERIFY_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY_16, VDEV_DEF_VRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write6 = { + .od_opcode = WRITE_6, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_6, 0x1F, + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write10 = { + .od_opcode = WRITE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_10, VDEV_DEF_WRPROTECT | 0x1A, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write12 = { + .od_opcode = WRITE_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_12, VDEV_DEF_WRPROTECT | 0x1A, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write16 = { + .od_opcode = WRITE_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_16, VDEV_DEF_WRPROTECT | 0x1A, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify10 = { + .od_opcode = WRITE_VERIFY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY, VDEV_DEF_WRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify12 = { + .od_opcode = WRITE_VERIFY_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY_12, VDEV_DEF_WRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify16 = { + .od_opcode = WRITE_VERIFY_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY_16, VDEV_DEF_WRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_same10 = { + .od_opcode = WRITE_SAME, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_SAME, VDEV_DEF_WRPROTECT | 0x8, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_same16 = { + .od_opcode = WRITE_SAME_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_SAME_16, VDEV_DEF_WRPROTECT | 0x8, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_toc = { + .od_opcode = READ_TOC, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_TOC, 0, 0xF, 0, 0, 0, 0xFF, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + #define SHARED_OPS \ [SYNCHRONIZE_CACHE] = vdisk_synchronize_cache, \ + [SYNCHRONIZE_CACHE_16] = vdisk_synchronize_cache, \ [MODE_SENSE] = vdisk_exec_mode_sense, \ [MODE_SENSE_10] = vdisk_exec_mode_sense, \ [MODE_SELECT] = vdisk_exec_mode_select, \ @@ -1420,10 +1737,39 @@ static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) [UNMAP] = vdisk_exec_unmap, \ [WRITE_SAME] = vdisk_exec_write_same, \ [WRITE_SAME_16] = vdisk_exec_write_same, \ + [COMPARE_AND_WRITE] = vdisk_exec_caw, \ [MAINTENANCE_IN] = vdisk_exec_maintenance_in, \ [SEND_DIAGNOSTIC] = vdisk_exec_send_diagnostic, \ [FORMAT_UNIT] = vdisk_exec_format_unit, +#define SHARED_OPCODE_DESCRIPTORS \ + &scst_op_descr_sync_cache10, \ + &scst_op_descr_sync_cache16, \ + &scst_op_descr_mode_sense6, \ + &scst_op_descr_mode_sense10, \ + &scst_op_descr_mode_select6, \ + &scst_op_descr_mode_select10, \ + &scst_op_descr_log_select, \ + &scst_op_descr_log_sense, \ + &scst_op_descr_start_stop_unit, \ + &scst_op_descr_read_capacity, \ + &scst_op_descr_send_diagnostic, \ + &scst_op_descr_rtpg, \ + &scst_op_descr_read6, \ + &scst_op_descr_read10, \ + &scst_op_descr_read12, \ + &scst_op_descr_read16, \ + &scst_op_descr_write6, \ + &scst_op_descr_write10, \ + &scst_op_descr_write12, \ + &scst_op_descr_write16, \ + &scst_op_descr_write_verify10, \ + &scst_op_descr_write_verify12, \ + &scst_op_descr_write_verify16, \ + &scst_op_descr_verify10, \ + &scst_op_descr_verify12, \ + &scst_op_descr_verify16, + static vdisk_op_fn blockio_ops[256] = { [READ_6] = blockio_exec_read, [READ_10] = blockio_exec_read, @@ -1451,7 +1797,6 @@ static vdisk_op_fn fileio_ops[256] = { [WRITE_10] = fileio_exec_write, [WRITE_12] = fileio_exec_write, [WRITE_16] = fileio_exec_write, - [COMPARE_AND_WRITE] = vdisk_exec_caw, [WRITE_VERIFY] = fileio_exec_write_verify, [WRITE_VERIFY_12] = fileio_exec_write_verify, [WRITE_VERIFY_16] = fileio_exec_write_verify, @@ -1473,9 +1818,52 @@ static vdisk_op_fn nullio_ops[256] = { [WRITE_VERIFY] = nullio_exec_write_verify, [WRITE_VERIFY_12] = nullio_exec_write_verify, [WRITE_VERIFY_16] = nullio_exec_write_verify, + [VERIFY] = nullio_exec_verify, + [VERIFY_12] = nullio_exec_verify, + [VERIFY_16] = nullio_exec_verify, SHARED_OPS }; +#define VDISK_OPCODE_DESCRIPTORS \ + /* &scst_op_descr_get_lba_status, */ \ + &scst_op_descr_read_capacity16, \ + &scst_op_descr_write_same10, \ + &scst_op_descr_write_same16, \ + &scst_op_descr_unmap, \ + &scst_op_descr_format_unit, \ + &scst_op_descr_cwr, + +static const struct scst_opcode_descriptor *vdisk_opcode_descriptors[] = { + SHARED_OPCODE_DESCRIPTORS + VDISK_OPCODE_DESCRIPTORS + SCST_OPCODE_DESCRIPTORS +}; + +static const struct scst_opcode_descriptor *vcdrom_opcode_descriptors[] = { + SHARED_OPCODE_DESCRIPTORS + &scst_op_descr_allow_medium_removal, + &scst_op_descr_read_toc, + SCST_OPCODE_DESCRIPTORS +}; + +static int vdisk_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt) +{ + *out_supp_opcodes = vdisk_opcode_descriptors; + *out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors); + return 0; +} + +static int vcdrom_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt) +{ + *out_supp_opcodes = vcdrom_opcode_descriptors; + *out_supp_opcodes_cnt = ARRAY_SIZE(vcdrom_opcode_descriptors); + return 0; +} + /* * Compute p->loff and p->fua. * Returns true for success or false otherwise and set error in the commeand. @@ -2248,9 +2636,9 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, (unsigned long long)start_lba, (unsigned long long)blocks); if (virt_dev->blockio) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) sector_t start_sector = start_lba << (cmd->dev->block_shift - 9); sector_t nr_sects = blocks << (cmd->dev->block_shift - 9); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) struct inode *inode = fd->f_dentry->d_inode; gfp_t gfp = cmd->cmd_gfp_mask; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31) @@ -3530,6 +3918,7 @@ out: static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p) { + /* Changing it don't forget to add it to vdisk_opcode_descriptors! */ return INVALID_OPCODE; } @@ -4237,7 +4626,11 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) bios++; need_new_bio = 0; bio->bi_end_io = blockio_endio; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + bio->bi_iter.bi_sector = lba_start0 << (block_shift - 9); +#else bio->bi_sector = lba_start0 << (block_shift - 9); +#endif bio->bi_bdev = bdev; bio->bi_private = blockio_work; /* @@ -4262,6 +4655,14 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) bio->bi_rw |= REQ_FUA; if (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + bio->bi_rw |= REQ_SYNC; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + bio->bi_rw |= 1 << BIO_RW_SYNCIO; +#else + bio->bi_rw |= 1 << BIO_RW_SYNC; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 bio->bi_rw |= REQ_META; @@ -4469,8 +4870,10 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, { DECLARE_COMPLETION_ONSTACK(c); struct block_device *bdev = virt_dev->bdev; + const bool is_vmalloc = is_vmalloc_addr(buf); struct bio *bio; void *p; + struct page *q; int max_nr_vecs, rc; unsigned bytes, off; ssize_t ret = -ENOMEM; @@ -4497,8 +4900,9 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, #endif for (p = buf; p < buf + len; p += bytes) { off = offset_in_page(p); - bytes = PAGE_SIZE - off; - rc = bio_add_page(bio, virt_to_page(p), bytes, off); + bytes = min_t(size_t, PAGE_SIZE - off, buf + len - p); + q = is_vmalloc ? vmalloc_to_page(p) : virt_to_page(p); + rc = bio_add_page(bio, q, bytes, off); if (WARN_ON_ONCE(rc < bytes)) goto free; } @@ -4566,7 +4970,7 @@ static ssize_t vdev_read_sync(struct scst_vdisk_dev *virt_dev, void *buf, if (virt_dev->nullio) return len; else if (virt_dev->blockio) - return blockio_rw_sync(virt_dev, buf, len, loff, 0/*read*/); + return blockio_rw_sync(virt_dev, buf, len, loff, READ_SYNC); else return fileio_read_sync(virt_dev->fd, buf, len, loff); } @@ -4574,21 +4978,12 @@ static ssize_t vdev_read_sync(struct scst_vdisk_dev *virt_dev, void *buf, static ssize_t vdev_write_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff) { - int rw; - - if (virt_dev->nullio) { + if (virt_dev->nullio) return len; - } else if (virt_dev->blockio) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) - rw = REQ_WRITE; -#else - rw = 1 << BIO_RW; -#endif - - return blockio_rw_sync(virt_dev, buf, len, loff, rw); - } else { + else if (virt_dev->blockio) + return blockio_rw_sync(virt_dev, buf, len, loff, WRITE_SYNC); + else return fileio_write_sync(virt_dev->fd, buf, len, loff); - } } static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) @@ -4725,7 +5120,10 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) goto out; } - WARN_ON_ONCE(length != 2 * data_len); + if (length != 2 * data_len) { + scst_set_invalid_field_in_cdb(cmd, 13, 0); + goto out; + } loff = p->loff; read = vdev_read_sync(virt_dev, read_buf, data_len, &loff); @@ -4806,6 +5204,11 @@ static enum compl_status_e nullio_exec_write_verify(struct vdisk_cmd_params *p) return CMD_SUCCEEDED; } +static enum compl_status_e nullio_exec_verify(struct vdisk_cmd_params *p) +{ + return CMD_SUCCEEDED; +} + static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd, struct scst_tgt_dev *tgt_dev) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index da49e0c15..b90f82447 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -88,6 +88,240 @@ static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, static void scst_free_descriptors(struct scst_cmd *cmd); +const struct scst_opcode_descriptor scst_op_descr_inquiry = { + .od_opcode = INQUIRY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { INQUIRY, 1, 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_inquiry); + +const struct scst_opcode_descriptor scst_op_descr_tur = { + .od_opcode = TEST_UNIT_READY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { TEST_UNIT_READY, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_tur); + +const struct scst_opcode_descriptor scst_op_descr_log_select = { + .od_opcode = LOG_SELECT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { LOG_SELECT, 3, 0xFF, 0xFF, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_log_select); + +const struct scst_opcode_descriptor scst_op_descr_log_sense = { + .od_opcode = LOG_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { LOG_SENSE, 1, 0xFF, 0xFF, 0, 0xFF, 0xFF, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_log_sense); + +const struct scst_opcode_descriptor scst_op_descr_mode_select6 = { + .od_opcode = MODE_SELECT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SELECT, 0x11, 0, 0, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_select6); + +const struct scst_opcode_descriptor scst_op_descr_mode_sense6 = { + .od_opcode = MODE_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SENSE, 8, 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_sense6); + +const struct scst_opcode_descriptor scst_op_descr_mode_select10 = { + .od_opcode = MODE_SELECT_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SELECT_10, 0x11, 0, 0, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_select10); + +const struct scst_opcode_descriptor scst_op_descr_mode_sense10 = { + .od_opcode = MODE_SENSE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SENSE_10, 0x18, 0xFF, 0xFF, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_sense10); + +const struct scst_opcode_descriptor scst_op_descr_rtpg = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_TARGET_PGS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, 0xE0|MI_REPORT_TARGET_PGS, 0, 0, + 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_rtpg); + +const struct scst_opcode_descriptor scst_op_descr_stpg = { + .od_opcode = MAINTENANCE_OUT, + .od_serv_action = MO_SET_TARGET_PGS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MO_SET_TARGET_PGS, 0, 0, 0, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_stpg); + +const struct scst_opcode_descriptor scst_op_descr_send_diagnostic = { + .od_opcode = SEND_DIAGNOSTIC, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SEND_DIAGNOSTIC, 0xF7, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_send_diagnostic); + +const struct scst_opcode_descriptor scst_op_descr_reserve6 = { + .od_opcode = RESERVE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RESERVE, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_reserve6); + +const struct scst_opcode_descriptor scst_op_descr_release6 = { + .od_opcode = RELEASE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RELEASE, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_release6); + +const struct scst_opcode_descriptor scst_op_descr_reserve10 = { + .od_opcode = RESERVE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RESERVE_10, 0, 0, 0, 0, 0, 0, 0, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_reserve10); + +const struct scst_opcode_descriptor scst_op_descr_release10 = { + .od_opcode = RELEASE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RELEASE_10, 0, 0, 0, 0, 0, 0, 0, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_release10); + +const struct scst_opcode_descriptor scst_op_descr_pr_in = { + .od_opcode = PERSISTENT_RESERVE_IN, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { PERSISTENT_RESERVE_IN, 0x1F, 0, 0, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_pr_in); + +const struct scst_opcode_descriptor scst_op_descr_pr_out = { + .od_opcode = PERSISTENT_RESERVE_OUT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { PERSISTENT_RESERVE_OUT, 0x1F, 0xFF, 0, 0, 0xFF, + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_pr_out); + +const struct scst_opcode_descriptor scst_op_descr_report_luns = { + .od_opcode = REPORT_LUNS, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { REPORT_LUNS, 0, 0xFF, 0, 0, 0, 0xFF, 0xFF, + 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_luns); + +const struct scst_opcode_descriptor scst_op_descr_request_sense = { + .od_opcode = REQUEST_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { REQUEST_SENSE, 1, 0, 0, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_request_sense); + +const struct scst_opcode_descriptor scst_op_descr_report_supp_tm_fns = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS, + 0x80, 0, 0, 0, 0xFF, 0xFF, 0xFF, + 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_supp_tm_fns); + +const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_SUPPORTED_OPERATION_CODES, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MI_REPORT_SUPPORTED_OPERATION_CODES, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_supp_opcodes); + struct scst_sdbops; static int get_cdb_info_len_10(struct scst_cmd *cmd, @@ -122,7 +356,7 @@ static int get_cdb_info_verify16(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_len_1(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); -static int get_cdb_info_lba_2_len_1_256(struct scst_cmd *cmd, +static int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_bidi_lba_4_len_2(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); @@ -313,9 +547,9 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_TEST_IO_IN_SIRQ_ALLOWED| #endif SCST_WRITE_EXCL_ALLOWED, - .info_lba_off = 2, .info_lba_len = 2, + .info_lba_off = 1, .info_lba_len = 3, .info_len_off = 4, .info_len_len = 1, - .get_cdb_info = get_cdb_info_lba_2_len_1_256}, + .get_cdb_info = get_cdb_info_lba_3_len_1_256}, {.ops = 0x08, .devkey = " MV O OV ", .info_op_name = "READ(6)", .info_data_direction = SCST_DATA_READ, @@ -343,9 +577,9 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_TEST_IO_IN_SIRQ_ALLOWED| #endif SCST_WRITE_MEDIUM, - .info_lba_off = 2, .info_lba_len = 2, + .info_lba_off = 1, .info_lba_len = 3, .info_len_off = 4, .info_len_len = 1, - .get_cdb_info = get_cdb_info_lba_2_len_1_256}, + .get_cdb_info = get_cdb_info_lba_3_len_1_256}, {.ops = 0x0A, .devkey = " M O OV ", .info_op_name = "WRITE(6)", .info_data_direction = SCST_DATA_WRITE, @@ -6618,10 +6852,11 @@ static int get_cdb_info_len_1(struct scst_cmd *cmd, return 0; } -static int get_cdb_info_lba_2_len_1_256(struct scst_cmd *cmd, +static int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { - cmd->lba = get_unaligned_be16(cmd->cdb + sdbops->info_lba_off); + cmd->lba = (cmd->cdb[sdbops->info_lba_off] & 0x1F) << 16; + cmd->lba |= get_unaligned_be16(cmd->cdb + sdbops->info_lba_off + 1); /* * From the READ(6) specification: a TRANSFER LENGTH field set to zero * specifies that 256 logical blocks shall be read. @@ -6900,7 +7135,8 @@ static int get_cdb_info_min(struct scst_cmd *cmd, break; case MI_REPORT_SUPPORTED_OPERATION_CODES: cmd->op_name = "REPORT SUPPORTED OPERATION CODES"; - cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED; + cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED | + SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; break; case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: cmd->op_name = "REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS"; diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 5ab3b6eb1..48c246d3d 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -2201,7 +2201,7 @@ void scst_pr_preempt(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) static void scst_cmd_done_pr_preempt(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context) { - void (*saved_cmd_done) (struct scst_cmd *cmd, int next_state, + void (*saved_cmd_done)(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context); TRACE_ENTRY(); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 705b24263..ff80f55d1 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -2111,6 +2111,208 @@ out_compl: return res; } +static int scst_report_supported_opcodes(struct scst_cmd *cmd) +{ + int res = SCST_EXEC_COMPLETED; + int length, buf_len, i, offs; + uint8_t *address; + uint8_t *buf; + bool inline_buf; + bool rctd = cmd->cdb[2] >> 7; + int options = cmd->cdb[2] & 7; + int req_opcode = cmd->cdb[3]; + int req_sa = get_unaligned_be16(&cmd->cdb[4]); + const struct scst_opcode_descriptor *op = NULL; + const struct scst_opcode_descriptor **supp_opcodes = NULL; + int supp_opcodes_cnt; + + TRACE_ENTRY(); + + if (cmd->devt->get_supported_opcodes == NULL) { + TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + goto out_compl; + } else { + int rc = cmd->devt->get_supported_opcodes(cmd, &supp_opcodes, + &supp_opcodes_cnt); + if (rc != 0) + goto out_compl; + } + + TRACE_DBG("cmd %p, options %d, req_opcode %x, req_sa %x, rctd %d", + cmd, options, req_opcode, req_sa, rctd); + + switch (options) { + case 0: /* all */ + buf_len = 4; + for (i = 0; i < supp_opcodes_cnt; i++) { + buf_len += 8; + if (rctd) + buf_len += 12; + } + break; + case 1: + buf_len = 0; + for (i = 0; i < supp_opcodes_cnt; i++) { + if (req_opcode == supp_opcodes[i]->od_opcode) { + op = supp_opcodes[i]; + if (op->od_serv_action_valid) { + TRACE(TRACE_MINOR, "Requested opcode %x " + "with unexpected service action " + "(dev %s, initiator %s)", + req_opcode, cmd->dev->virt_name, + cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + buf_len = 4 + op->od_cdb_size; + if (rctd) + buf_len += 12; + break; + } + } + if (op == NULL) { + TRACE(TRACE_MINOR, "Requested opcode %x not found " + "(dev %s, initiator %s)", req_opcode, + cmd->dev->virt_name, cmd->sess->initiator_name); + buf_len = 4; + } + break; + case 2: + buf_len = 0; + for (i = 0; i < supp_opcodes_cnt; i++) { + if (req_opcode == supp_opcodes[i]->od_opcode) { + op = supp_opcodes[i]; + if (!op->od_serv_action_valid) { + TRACE(TRACE_MINOR, "Requested opcode %x " + "without expected service action " + "(dev %s, initiator %s)", + req_opcode, cmd->dev->virt_name, + cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + if (req_sa != op->od_serv_action) { + op = NULL; /* reset it */ + continue; + } + buf_len = 4 + op->od_cdb_size; + if (rctd) + buf_len += 12; + break; + } + } + if (op == NULL) { + TRACE(TRACE_MINOR, "Requested opcode %x/%x not found " + "(dev %s, initiator %s)", req_opcode, req_sa, + cmd->dev->virt_name, cmd->sess->initiator_name); + buf_len = 4; + } + break; + default: + PRINT_ERROR("REPORT SUPPORTED OPERATION CODES: REPORTING OPTIONS " + "%x not supported (dev %s, initiator %s)", options, + cmd->dev->virt_name, cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + + length = scst_get_buf_full_sense(cmd, &address); + TRACE_DBG("length %d, buf_len %d, op %p", length, buf_len, op); + if (unlikely(length <= 0)) + goto out_compl; + + if (length >= buf_len) { + buf = address; + inline_buf = true; + } else { + buf = vmalloc(buf_len); /* it can be big */ + if (buf == NULL) { + PRINT_ERROR("Unable to allocate REPORT SUPPORTED " + "OPERATION CODES buffer with size %d", buf_len); + scst_set_busy(cmd); + goto out_err_put; + } + inline_buf = false; + } + + memset(buf, 0, buf_len); + + switch (options) { + case 0: /* all */ + put_unaligned_be32(buf_len - 3, &buf[0]); + offs = 4; + for (i = 0; i < supp_opcodes_cnt; i++) { + op = supp_opcodes[i]; + buf[offs] = op->od_opcode; + if (op->od_serv_action_valid) { + put_unaligned_be16(op->od_serv_action, &buf[offs + 2]); + buf[offs + 5] |= 1; + } + put_unaligned_be16(op->od_cdb_size, &buf[offs + 6]); + offs += 8; + if (rctd) { + buf[(offs - 8) + 5] |= 2; + buf[offs + 1] = 0xA; + buf[offs + 3] = op->od_comm_specific_timeout; + put_unaligned_be32(op->od_nominal_timeout, &buf[offs + 4]); + put_unaligned_be32(op->od_recommended_timeout, &buf[offs + 8]); + offs += 12; + } + } + break; + case 1: + case 2: + if (op != NULL) { + buf[1] |= op->od_support; + put_unaligned_be16(op->od_cdb_size, &buf[2]); + memcpy(&buf[4], op->od_cdb_usage_bits, op->od_cdb_size); + if (rctd) { + buf[1] |= 0x80; + offs = 4 + op->od_cdb_size; + buf[offs + 1] = 0xA; + buf[offs + 3] = op->od_comm_specific_timeout; + put_unaligned_be32(op->od_nominal_timeout, &buf[offs + 4]); + put_unaligned_be32(op->od_recommended_timeout, &buf[offs + 8]); + } + } + break; + default: + sBUG_ON(1); + goto out_compl; + } + + if (length > buf_len) + length = buf_len; + if (!inline_buf) { + memcpy(address, buf, length); + vfree(buf); + } + + scst_put_buf_full(cmd, address); + if (length < cmd->resp_data_len) + scst_set_resp_data_len(cmd, length); + +out_compl: + if ((supp_opcodes != NULL) && (cmd->devt->put_supported_opcodes != NULL)) + cmd->devt->put_supported_opcodes(cmd, supp_opcodes, supp_opcodes_cnt); + + cmd->completed = 1; + + /* Report the result */ + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); + + TRACE_EXIT_RES(res); + return res; + +out_err_put: + scst_put_buf_full(cmd, address); + goto out_compl; +} + static int scst_maintenance_in(struct scst_cmd *cmd) { int res; @@ -2121,6 +2323,9 @@ static int scst_maintenance_in(struct scst_cmd *cmd) case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: res = scst_report_supported_tm_fns(cmd); break; + case MI_REPORT_SUPPORTED_OPERATION_CODES: + res = scst_report_supported_opcodes(cmd); + break; default: res = SCST_EXEC_NOT_COMPLETED; break; @@ -6880,7 +7085,7 @@ restart: */ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic, const char *initiator_name, void *tgt_priv, void *result_fn_data, - void (*result_fn) (struct scst_session *sess, void *data, int result)) + void (*result_fn)(struct scst_session *sess, void *data, int result)) { struct scst_session *sess; int res; @@ -6978,7 +7183,7 @@ EXPORT_SYMBOL(scst_register_session_non_gpl); * Otherwise, your target driver could wait for those commands forever. */ void scst_unregister_session(struct scst_session *sess, int wait, - void (*unreg_done_fn) (struct scst_session *sess)) + void (*unreg_done_fn)(struct scst_session *sess)) { unsigned long flags; DECLARE_COMPLETION_ONSTACK(c); @@ -7191,8 +7396,8 @@ static struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess, * Returns the command on success or NULL otherwise. */ struct scst_cmd *scst_find_cmd(struct scst_session *sess, void *data, - int (*cmp_fn) (struct scst_cmd *cmd, - void *data)) + int (*cmp_fn)(struct scst_cmd *cmd, + void *data)) { struct scst_cmd *cmd = NULL; unsigned long flags = 0; diff --git a/scst_local/in-tree/Makefile-3.14 b/scst_local/in-tree/Makefile-3.14 new file mode 100644 index 000000000..8cbbbff63 --- /dev/null +++ b/scst_local/in-tree/Makefile-3.14 @@ -0,0 +1,2 @@ +obj-$(CONFIG_SCST_LOCAL) += scst_local.o + diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm index dc2748c40..47e9ad172 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm @@ -741,7 +741,7 @@ sub luns { close $lHandle; - return (\%luns, undef); + return \%luns; } sub aluaAttributes { diff --git a/scstadmin/scstadmin.sysfs/scstadmin b/scstadmin/scstadmin.sysfs/scstadmin index 6e976e93b..8ee27556c 100755 --- a/scstadmin/scstadmin.sysfs/scstadmin +++ b/scstadmin/scstadmin.sysfs/scstadmin @@ -3414,11 +3414,11 @@ sub listAttributes { my $found = FALSE; - foreach my $attribute (keys %{$attributes}) { + foreach my $attribute (sort keys %{$attributes}) { my $first = TRUE; if (defined($$attributes{$attribute}->{'keys'})) { - foreach my $key (keys %{$$attributes{$attribute}->{'keys'}}) { + foreach my $key (sort keys %{$$attributes{$attribute}->{'keys'}}) { my $value = $$attributes{$attribute}->{'keys'}->{$key}->{'value'}; my $static = ($$attributes{$attribute}->{'static'}) ? 'No' : 'Yes'; $value = '' if ($value eq ''); diff --git a/srpt/README b/srpt/README index 01297583d..f605758b8 100644 --- a/srpt/README +++ b/srpt/README @@ -1,11 +1,10 @@ SCSI RDMA Protocol (SRP) Target driver for Linux ================================================= -The SRP target driver has been designed to work on top of the Linux -InfiniBand kernel drivers -- either the InfiniBand drivers included -with a Linux distribution or the OFED InfiniBand drivers. For more -information about using the SRP target driver in combination with -OFED, see also README.ofed. +The SRP target driver has been designed to work on top of the Linux RDMA +kernel drivers -- either the RDMA drivers included with a Linux distribution +or the OFED RDMA drivers. For more information about using the SRP target +driver in combination with OFED, see also README.ofed. The SRP target driver has been implemented as an SCST driver. This makes it possible to support a lot of I/O modes on real and virtual @@ -30,9 +29,13 @@ Installation Building and installing the SRP target driver is possible as follows: cd ${SCST_DIR} - make -s scst_clean scst scst_install - make -s srpt_clean srpt srpt_install - make -s scstadm scstadm_install + if type -p rpm >/dev/null; then + make -s rpm + sudo rpm -U rpmbuilddir/RPMS/*/*rpm scstadmin/rpmbuilddir/RPMS/*/*rpm + else + make -s scst_clean srpt_clean scst srpt scstadmin + sudo make -s scst_install srpt_install scstadm_install + fi The ib_srpt kernel module supports the following parameters: * one_target_per_port (boolean) and @@ -48,6 +51,11 @@ The ib_srpt kernel module supports the following parameters: use_node_guid_in_target_name are false. Mode (2) is choosen if one_target_per_port is false and use_node_guid_in_target_name is true. Mode (3) is choosen if one_target_per_port is true. +* rdma_cm_port (number) + A 16-bit number that specifies the port number to be registered via the + RDMA/CM. Must be specified to make communication over RoCE or iWARP + possible. If this parameter is zero (the default value) the SRP target + driver does not register with the RDMA/CM. * srp_max_req_size (number) Maximum size of an SRP control message in bytes. Examples of SRP control messages are: login request, logout request, data transfer request, ... @@ -109,6 +117,11 @@ Now verify that loading the configuration from file works correctly: /etc/init.d/scst reload +Note: when using InfiniBand loading the ib_ipoib kernel module and assigning +an IP address to each IPoIB interface is only needed when using the RDMA/CM. +When using the IB/CM however, it is allowed but not necessary to load the +ib_ipoib kernel module. + Configuring the SRP Initiator System ------------------------------------ @@ -117,7 +130,8 @@ First of all, load the SRP kernel module as follows: modprobe ib_srp -Next, discover the new SRP target by running the srp_daemon command: +Next, when using InfiniBand, discover the new SRP target by running the +srp_daemon command: for d in /dev/infiniband/umad*; do srp_daemon -oacd$d; done @@ -138,7 +152,20 @@ The meaning of the parameters in the above command is as follows: * pkey: IB partition key (P_Key) of the target to connect to. * service_id: must match ioc_guid. -Target GIDs can be queried e.g. via sysfs: +When using RoCE or iWARP, log in to the target system to determine the id_ext +and ioc_guid parameters and use these to log in. An example: + + [ target system ] + # sed 's/,\(pkey\|dgid\|ioc_guid\)=[^,]*//g' $(find /sys/kernel/scst_tgt/targets/ib_srpt -name login_info) | uniq + id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 + + [ initiator system ] + echo dest=192.168.5.1:5000,id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 + >/sys/class/infiniband_srp/srp-mlx4_0-1/add_target + echo dest=192.168.6.1:5000,id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 + >/sys/class/infiniband_srp/srp-mlx4_0-2/add_target + +Initiator port GIDs can be queried e.g. via sysfs: $ for f in /sys/devices/*/*/*/infiniband/*/ports/*/gids/0; do echo $f; \ cat $f | sed 's/://g'; done @@ -166,9 +193,9 @@ Target names The name assigned by the ib_srpt target driver to an SCST target is either ib_srpt_target_, the node GUID of a HCA in hexadecimal form with a colon -after every fourth digit or the port GUID with a colon afer every fourth -digit. The HCA node and port GUIDs can be obtained via the ibv_devinfo -command. An example: +after every fourth digit or the port GID with a colon afer every fourth +digit. The HCA node GUID and the port GIDs can be obtained via the +ibv_devinfo command. An example: # ibv_devinfo -v | grep -E '[^a-z]port:|guid|GID' node_guid: 0002:c903:0005:f34e @@ -359,7 +386,8 @@ Performance Notes - Initiator Side * The SRP initiator limits by default the queue depth to 64 commands. If your workload benefits from a larger queue depth, enlarge the queue depth by - setting the max_cmd_per_lun parameter in the SRP login string. + setting the max_cmd_per_lun and queue_size parameters in the SRP login + string. * The following parameters have a small but measurable impact on SRP performance: @@ -372,7 +400,7 @@ Performance Notes - Both Sides ------------------------------ * Disabling CONFIG_SCHED_DEBUG and CONFIG_SCHEDSTATS in the kernel config - helps. + improves performance. * Disable CONFIG_IRQSOFF_TRACER such that CONFIG_TRACE_IRQFLAGS is disabled. @@ -421,5 +449,4 @@ A: This means that you are using a system on which OFED has been installed but Feedback -------- -Send questions about this driver to scst-devel@lists.sourceforge.net, CC: -Vu Pham and Bart Van Assche . +Send questions about this driver to scst-devel@lists.sourceforge.net. diff --git a/srpt/patches/kernel-3.14-pre-cflags.patch b/srpt/patches/kernel-3.14-pre-cflags.patch new file mode 100644 index 000000000..3964ee179 --- /dev/null +++ b/srpt/patches/kernel-3.14-pre-cflags.patch @@ -0,0 +1,12 @@ +diff --git a/Makefile b/Makefile +index 540f7b2..078307f 100644 +--- a/Makefile ++++ b/Makefile +@@ -361,6 +361,7 @@ USERINCLUDE := \ + # Use LINUXINCLUDE when you must reference the include/ directory. + # Needed to be compatible with the O= option + LINUXINCLUDE := \ ++ $(PRE_CFLAGS) \ + -I$(srctree)/arch/$(hdr-arch)/include \ + -Iarch/$(hdr-arch)/include/generated \ + $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/session-management.txt b/srpt/session-management.txt index 752218787..89147cca8 100644 --- a/srpt/session-management.txt +++ b/srpt/session-management.txt @@ -2,7 +2,8 @@ ============================== The following actions related to SRP sessions can all occur concurrently: -* IB communication manager (CM) invokes srpt_cm_handler(). +* The communication manager invokes either srpt_ib_cm_handler() or + srpt_rdma_cm_handler(). * HCA driver invokes the queue pair (QP) completion handler srpt_completion(). * HCA driver invokes the QP async event handler srpt_qp_event(). * HCA transfers data between initiator and target via RDMA. @@ -11,7 +12,7 @@ The following actions related to SRP sessions can all occur concurrently: The actions that occur over the lifetime of a session are as follows: - A REQ message is received from the initiator. -- srpt_cm_req_recv() is invoked and allocates a queue pair and creates a +- srpt_cm_req_recv() is invoked, allocates a queue pair and creates a completion thread. - If the connection request is not accepted, a REJ message is sent and srpt_close_ch() is invoked. The srpt_close_ch() call causes the completion @@ -21,14 +22,12 @@ The actions that occur over the lifetime of a session are as follows: invoked. That function changes the queue pair state into RTS, the channel state into CH_LIVE and wakes up the completion thread. - RDMA communication starts and continues until either a DREQ message is - received or sent. The function ib_send_cm_dreq() can get invoked - either because a target port is disabled or from inside the - srpt_close_session() function. + received or sent. A DREQ is sent either because a target port is disabled or + from inside the srpt_close_session() function. - After a DREQ has been sent either a DREP will be received (srpt_cm_drep_recv()) or the TimeWait state will be reached and will be left (srpt_cm_timewait_exit()). -- srpt_cm_dre[pq]_recv() and srpt_cm_timewait_exit() all invoke - srpt_close_ch(). +- srpt_cm_drep_recv() and srpt_cm_timewait_exit() invoke srpt_close_ch(). - srpt_close_ch() changes the channel state into CH_DISCONNECTING, the queue pair state into IB_QPS_ERR and queues a zero-length write. - Upon receipt of the zero-length write completion the channel state is diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 05bcbc4b5..17ad77e70 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #if defined(CONFIG_SCST_PROC) #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -98,6 +99,10 @@ module_param(trace_flag, long, 0644); MODULE_PARM_DESC(trace_flag, "SCST trace flags."); #endif +static u16 rdma_cm_port; +module_param(rdma_cm_port, short, 0444); +MODULE_PARM_DESC(rdma_cm_port, "Port number RDMA/CM will bind to."); + static unsigned srp_max_rdma_size = DEFAULT_MAX_RDMA_SIZE; module_param(srp_max_rdma_size, int, 0644); MODULE_PARM_DESC(srp_max_rdma_size, @@ -154,6 +159,8 @@ module_param(one_target_per_port, bool, 0444); MODULE_PARM_DESC(one_target_per_port, "One SCST target per HCA port instead of one per HCA."); +static struct rdma_cm_id *rdma_cm_id; + static int srpt_get_u64_x(char *buffer, struct kernel_param *kp) { return sprintf(buffer, "0x%016llx", *(u64 *)kp->arg); @@ -339,14 +346,17 @@ static const char *get_ch_state_name(enum rdma_ch_state s) */ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) { - TRACE_DBG("QP event %d on cm_id=%p sess_name=%s state=%s", - event->event, ch->cm_id, ch->sess_name, + TRACE_DBG("QP event %d on ch=%p sess_name=%s state=%s", + event->event, ch, ch->sess_name, get_ch_state_name(ch->state)); switch (event->event) { case IB_EVENT_COMM_EST: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) || defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19) - ib_cm_notify(ch->cm_id, event->event); + if (ch->using_rdma_cm) + rdma_notify(ch->rdma_cm.cm_id, event->event); + else + ib_cm_notify(ch->ib_cm.cm_id, event->event); #else /* Vanilla 2.6.19 kernel (or before) without OFED. */ PRINT_ERROR("how to perform ib_cm_notify() on a" @@ -646,17 +656,6 @@ static int srpt_refresh_port(struct srpt_port *sport) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) /* commit a3f5adaf4 */ - switch (rdma_port_get_link_layer(sport->sdev->device, sport->port)) { - case IB_LINK_LAYER_UNSPECIFIED: - case IB_LINK_LAYER_INFINIBAND: - break; - case IB_LINK_LAYER_ETHERNET: - default: - return 0; - } -#endif - memset(&port_modify, 0, sizeof(port_modify)); port_modify.set_port_cap_mask = IB_PORT_DEVICE_MGMT_SUP; port_modify.clr_port_cap_mask = 0; @@ -1116,15 +1115,20 @@ static int srpt_init_ch_qp(struct srpt_rdma_ch *ch, struct ib_qp *qp) struct ib_qp_attr *attr; int ret; + WARN_ON_ONCE(ch->using_rdma_cm); + attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; attr->qp_state = IB_QPS_INIT; - attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_READ | - IB_ACCESS_REMOTE_WRITE; attr->port_num = ch->sport->port; - attr->pkey_index = ch->pkey_index; + + ret = ib_find_cached_pkey(ch->sport->sdev->device, ch->sport->port, + ch->pkey, &attr->pkey_index); + if (ret < 0) + PRINT_ERROR("Translating pkey %#x failed (%d) - using index 0", + ch->pkey, ret); ret = ib_modify_qp(qp, attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS | IB_QP_PORT | @@ -1147,15 +1151,18 @@ static int srpt_ch_qp_rtr(struct srpt_rdma_ch *ch, struct ib_qp *qp) int attr_mask; int ret; + WARN_ON_ONCE(ch->using_rdma_cm); + attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; attr->qp_state = IB_QPS_RTR; - ret = ib_cm_init_qp_attr(ch->cm_id, attr, &attr_mask); + ret = ib_cm_init_qp_attr(ch->ib_cm.cm_id, attr, &attr_mask); if (ret) goto out; + attr->qp_access_flags = 0; attr->max_dest_rd_atomic = 4; ret = ib_modify_qp(qp, attr, attr_mask); @@ -1177,45 +1184,21 @@ static int srpt_ch_qp_rts(struct srpt_rdma_ch *ch, struct ib_qp *qp) struct ib_qp_attr *attr; int attr_mask; int ret; - uint64_t T_tr_ns, max_compl_time_ms; - uint32_t T_tr_ms; + + WARN_ON_ONCE(ch->using_rdma_cm); attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; attr->qp_state = IB_QPS_RTS; - ret = ib_cm_init_qp_attr(ch->cm_id, attr, &attr_mask); + ret = ib_cm_init_qp_attr(ch->ib_cm.cm_id, attr, &attr_mask); if (ret) goto out; + attr->qp_access_flags = 0; attr->max_rd_atomic = 4; - /* - * From IBTA C9-140: Transport Timer timeout interval - * T_tr = 4.096 us * 2**(local ACK timeout) where the local ACK timeout - * is a five-bit value, with zero meaning that the timer is disabled. - */ - WARN_ON(attr->timeout >= (1 << 5)); - if (attr->timeout) { - T_tr_ns = 1ULL << (12 + attr->timeout); - max_compl_time_ms = attr->retry_cnt * 4 * T_tr_ns; - do_div(max_compl_time_ms, 1000000); - T_tr_ms = T_tr_ns; - do_div(T_tr_ms, 1000000); - TRACE_DBG("Session %s: QP local ack timeout = %d or T_tr =" - " %u ms; retry_cnt = %d; max compl. time = %d ms", - ch->sess_name, attr->timeout, T_tr_ms, - attr->retry_cnt, (unsigned)max_compl_time_ms); - - if (max_compl_time_ms >= RDMA_COMPL_TIMEOUT_S * 1000) { - PRINT_ERROR("Maximum RDMA completion time (%d ms)" - " exceeds ib_srpt timeout (%d ms)", - (unsigned)max_compl_time_ms, - 1000 * RDMA_COMPL_TIMEOUT_S); - } - } - ret = ib_modify_qp(qp, attr, attr_mask); out: @@ -1377,7 +1360,7 @@ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, TRACE_EXIT(); } -void srpt_on_abort_cmd(struct scst_cmd *cmd) +static void srpt_on_abort_cmd(struct scst_cmd *cmd) { struct srpt_send_ioctx *ioctx = scst_cmd_get_tgt_priv(cmd); struct srpt_rdma_ch *ch = ioctx->ch; @@ -1730,9 +1713,9 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch, srp_tsk = recv_ioctx->ioctx.buf; TRACE_DBG("recv_tsk_mgmt= %d for task_tag= %lld" - " using tag= %lld cm_id= %p sess= %p", + " using tag= %lld ch= %p sess= %p", srp_tsk->tsk_mgmt_func, srp_tsk->task_tag, srp_tsk->tag, - ch->cm_id, ch->scst_sess); + ch, ch->scst_sess); send_ioctx->tsk_mgmt.tag = srp_tsk->tag; @@ -2043,20 +2026,20 @@ static void srpt_unreg_sess(struct scst_session *scst_sess) sdev, ch->rq_size, ch->max_rsp_size, DMA_TO_DEVICE); - /* - * If the connection is still established, ib_destroy_cm_id() will - * send a DREQ. - */ - ib_destroy_cm_id(ch->cm_id); + /* Wait until CM callbacks have finished and prevent new callbacks. */ + if (ch->using_rdma_cm) + rdma_destroy_id(ch->rdma_cm.cm_id); + else + ib_destroy_cm_id(ch->ib_cm.cm_id); /* * Invoke wake_up() inside the lock to avoid that srpt_tgt disappears * after list_del() and before wake_up() has been invoked. */ - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_del(&ch->list); wake_up(&srpt_tgt->ch_releaseQ); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); kref_put(&ch->kref, srpt_free_ch); } @@ -2146,36 +2129,42 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) WARN_ON(ch->max_sge < 1); qp_init->cap.max_send_sge = ch->max_sge; - ch->qp = ib_create_qp(sdev->pd, qp_init); - if (IS_ERR(ch->qp)) { - ret = PTR_ERR(ch->qp); - PRINT_ERROR("failed to create_qp ret= %d", ret); - goto err_destroy_cq; + if (ch->using_rdma_cm) { + ret = rdma_create_qp(ch->rdma_cm.cm_id, sdev->pd, qp_init); + ch->qp = ch->rdma_cm.cm_id->qp; + if (ret) + PRINT_ERROR("failed to create queue pair (%d)", ret); + } else { + ch->qp = ib_create_qp(sdev->pd, qp_init); + if (!IS_ERR(ch->qp)) { + ret = srpt_init_ch_qp(ch, ch->qp); + if (ret) { + PRINT_ERROR("srpt_init_ch_qp(%#x) failed (%d)", + ch->qp->qp_num, ret); + ib_destroy_qp(ch->qp); + } + } else { + ret = PTR_ERR(ch->qp); + PRINT_ERROR("failed to create queue pair (%d)", ret); + } } + if (ret) + goto err_destroy_cq; TRACE_DBG("qp_num = %#x", ch->qp->qp_num); atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr); - TRACE_DBG("%s: max_cqe= %d max_sge= %d sq_size = %d" - " cm_id= %p", __func__, ch->cq->cqe, - qp_init->cap.max_send_sge, qp_init->cap.max_send_wr, - ch->cm_id); - - ret = srpt_init_ch_qp(ch, ch->qp); - if (ret) { - PRINT_ERROR("srpt_init_ch_qp(%#x) failed (%d)", ch->qp->qp_num, - ret); - goto err_destroy_qp; - } + TRACE_DBG("%s: max_cqe= %d max_sge= %d sq_size = %d ch= %p", __func__, + ch->cq->cqe, qp_init->cap.max_send_sge, + qp_init->cap.max_send_wr, ch); out: kfree(qp_init); return ret; -err_destroy_qp: - ib_destroy_qp(ch->qp); err_destroy_cq: + ch->qp = NULL; ib_destroy_cq(ch->cq); goto out; } @@ -2186,8 +2175,23 @@ static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) ib_destroy_cq(ch->cq); } +static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) +{ + int ret; + + if (ch->using_rdma_cm) { + ret = rdma_disconnect(ch->rdma_cm.cm_id); + } else { + ret = ib_send_cm_dreq(ch->ib_cm.cm_id, NULL, 0); + if (ret < 0) + ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0); + } + + return ret; +} + /** - * __srpt_close_ch() - Close an RDMA channel. + * srpt_close_ch() - Close an RDMA channel. * * Make sure all resources associated with the channel will be deallocated at * an appropriate time. @@ -2195,22 +2199,14 @@ static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) * Returns true if and only if the channel state has been modified from * CH_CONNECTING or CH_LIVE into CH_DISCONNECTING. */ -static bool __srpt_close_ch(struct srpt_rdma_ch *ch) - __releases(&ch->srpt_tgt->spinlock) - __acquires(&ch->srpt_tgt->spinlock) +static bool srpt_close_ch(struct srpt_rdma_ch *ch) { - struct srpt_tgt *srpt_tgt = ch->srpt_tgt; int ret; bool was_live; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - lockdep_assert_held(&srpt_tgt->spinlock); -#endif - was_live = srpt_set_ch_state(ch, CH_DISCONNECTING); if (was_live) { kref_get(&ch->kref); - spin_unlock_irq(&srpt_tgt->spinlock); ret = srpt_ch_qp_err(ch); if (ret < 0) @@ -2225,43 +2221,27 @@ static bool __srpt_close_ch(struct srpt_rdma_ch *ch) } kref_put(&ch->kref, srpt_free_ch); - - spin_lock_irq(&srpt_tgt->spinlock); } return was_live; } -/** - * srpt_close_ch() - Close an RDMA channel. - */ -static void srpt_close_ch(struct srpt_rdma_ch *ch) -{ - struct srpt_tgt *srpt_tgt = ch->srpt_tgt; - - spin_lock_irq(&srpt_tgt->spinlock); - __srpt_close_ch(ch); - spin_unlock_irq(&srpt_tgt->spinlock); -} - static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) { struct srpt_nexus *nexus; struct srpt_rdma_ch *ch; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - lockdep_assert_held(&srpt_tgt->spinlock); + lockdep_assert_held(&srpt_tgt->mutex); #endif -restart: list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { list_for_each_entry(ch, &nexus->ch_list, list) { - if (ib_send_cm_dreq(ch->cm_id, NULL, 0) < 0) + if (srpt_disconnect_ch(ch) < 0) continue; PRINT_INFO("Closing channel %s because target %s has" " been disabled", ch->sess_name, srpt_tgt->scst_tgt->tgt_name); - goto restart; } } } @@ -2287,13 +2267,13 @@ static struct srpt_tgt *srpt_convert_scst_tgt(struct scst_tgt *scst_tgt) * it does not yet exist. */ static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, - u8 i_port_id[16], u8 t_port_id[16]) + const u8 i_port_id[16], + const u8 t_port_id[16]) { - unsigned long flags; struct srpt_nexus *nexus = NULL, *tmp_nexus = NULL, *n; for (;;) { - spin_lock_irqsave(&srpt_tgt->spinlock, flags); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry(n, &srpt_tgt->nexus_list, entry) { if (memcmp(n->i_port_id, i_port_id, 16) == 0 && memcmp(n->t_port_id, t_port_id, 16) == 0) { @@ -2305,7 +2285,7 @@ static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, list_add_tail(&tmp_nexus->entry, &srpt_tgt->nexus_list); swap(nexus, tmp_nexus); } - spin_unlock_irqrestore(&srpt_tgt->spinlock, flags); + mutex_unlock(&srpt_tgt->mutex); if (nexus) break; @@ -2341,11 +2321,11 @@ static int srpt_enable_target(struct scst_tgt *scst_tgt, bool enable) PRINT_INFO("%s target %s", enable ? "Enabling" : "Disabling", scst_tgt->tgt_name); - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); srpt_tgt->enabled = enable; if (!enable) __srpt_close_all_ch(srpt_tgt); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); res = 0; @@ -2370,19 +2350,23 @@ static bool srpt_is_target_enabled(struct scst_tgt *scst_tgt) * Ownership of the cm_id is transferred to the SCST session if this function * returns zero. Otherwise the caller remains the owner of cm_id. */ -static int srpt_cm_req_recv(struct ib_cm_id *cm_id, - struct ib_cm_req_event_param *param, - void *private_data) +static int srpt_cm_req_recv(struct srpt_device *const sdev, + struct ib_cm_id *ib_cm_id, + struct rdma_cm_id *rdma_cm_id, + u8 port_num, __be16 pkey, + const struct srp_login_req *req) { - struct srpt_device *const sdev = cm_id->context; - struct srpt_port *const sport = &sdev->port[param->port - 1]; + struct srpt_port *const sport = &sdev->port[port_num - 1]; + const __be16 *const raw_port_gid = (__be16 *)sport->gid.raw; struct srpt_tgt *const srpt_tgt = one_target_per_port ? &sport->srpt_tgt : &sdev->srpt_tgt; struct srpt_nexus *nexus; - struct srp_login_req *req; struct srp_login_rsp *rsp = NULL; struct srp_login_rej *rej = NULL; - struct ib_cm_rep_param *rep_param = NULL; + union { + struct rdma_conn_param rdma_cm; + struct ib_cm_rep_param ib_cm; + } *rep_param = NULL; struct srpt_rdma_ch *ch = NULL; struct task_struct *thread; u32 it_iu_len; @@ -2392,16 +2376,14 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, EXTRACHECKS_WARN_ON_ONCE(irqs_disabled()); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) - WARN_ON(!sdev || !private_data); - if (!sdev || !private_data) + WARN_ON(!sdev || !req); + if (!sdev || !req) return -EINVAL; #else - if (WARN_ON(!sdev || !private_data)) + if (WARN_ON(!sdev || !req)) return -EINVAL; #endif - req = (struct srp_login_req *)private_data; - it_iu_len = be32_to_cpu(req->req_it_iu_len); PRINT_INFO("Received SRP_LOGIN_REQ with i_port_id" @@ -2426,15 +2408,15 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, be16_to_cpu(*(__be16 *)&req->target_port_id[12]), be16_to_cpu(*(__be16 *)&req->target_port_id[14]), it_iu_len, - param->port, - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[0]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[2]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[4]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[6]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[8]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[10]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[12]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[14])); + port_num, + be16_to_cpu(raw_port_gid[0]), + be16_to_cpu(raw_port_gid[1]), + be16_to_cpu(raw_port_gid[2]), + be16_to_cpu(raw_port_gid[3]), + be16_to_cpu(raw_port_gid[4]), + be16_to_cpu(raw_port_gid[5]), + be16_to_cpu(raw_port_gid[6]), + be16_to_cpu(raw_port_gid[7])); nexus = srpt_get_nexus(srpt_tgt, req->initiator_port_id, req->target_port_id); @@ -2487,19 +2469,18 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, } kref_init(&ch->kref); - ret = ib_find_pkey(sdev->device, sport->port, - be16_to_cpu(param->primary_path->pkey), - &ch->pkey_index); - if (ret < 0) { - ch->pkey_index = 0; - PRINT_ERROR("Translating pkey %#x failed (%d) - using index 0", - be16_to_cpu(param->primary_path->pkey), ret); - } + ch->pkey = be16_to_cpu(pkey); ch->nexus = nexus; ch->sport = sport; ch->srpt_tgt = srpt_tgt; - ch->cm_id = cm_id; - cm_id->context = ch; + if (ib_cm_id) { + ch->ib_cm.cm_id = ib_cm_id; + ib_cm_id->context = ch; + } else { + ch->using_rdma_cm = true; + ch->rdma_cm.cm_id = rdma_cm_id; + rdma_cm_id->context = ch; + } /* * Avoid QUEUE_FULL conditions by limiting the number of buffers used * for the SRP protocol to the SCST SCSI command queue size. @@ -2533,18 +2514,16 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, } if (one_target_per_port) { - __be16 *const raw_gid = (__be16 *)param->primary_path->dgid.raw; - snprintf(ch->sess_name, sizeof(ch->sess_name), "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", - be16_to_cpu(raw_gid[0]), - be16_to_cpu(raw_gid[1]), - be16_to_cpu(raw_gid[2]), - be16_to_cpu(raw_gid[3]), - be16_to_cpu(raw_gid[4]), - be16_to_cpu(raw_gid[5]), - be16_to_cpu(raw_gid[6]), - be16_to_cpu(raw_gid[7])); + be16_to_cpu(raw_port_gid[0]), + be16_to_cpu(raw_port_gid[1]), + be16_to_cpu(raw_port_gid[2]), + be16_to_cpu(raw_port_gid[3]), + be16_to_cpu(raw_port_gid[4]), + be16_to_cpu(raw_port_gid[5]), + be16_to_cpu(raw_port_gid[6]), + be16_to_cpu(raw_port_gid[7])); } else if (use_port_guid_in_session_name) { /* * If the kernel module parameter use_port_guid_in_session_name @@ -2556,7 +2535,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, snprintf(ch->sess_name, sizeof(ch->sess_name), "0x%016llx%016llx", be64_to_cpu(*(__be64 *) - &sdev->port[param->port - 1].gid.raw[8]), + &sdev->port[port_num - 1].gid.raw[8]), be64_to_cpu(*(__be64 *)(nexus->i_port_id + 8))); } else { /* @@ -2590,20 +2569,18 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, goto unreg_ch; } - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); if ((req->req_flags & SRP_MTCH_ACTION) == SRP_MULTICHAN_SINGLE) { struct srpt_rdma_ch *ch2; rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_NO_CHAN; -restart: list_for_each_entry(ch2, &nexus->ch_list, list) { - if (ib_send_cm_dreq(ch2->cm_id, NULL, 0) < 0) + if (srpt_disconnect_ch(ch2) < 0) continue; PRINT_INFO("Relogin - closed existing channel %s", ch2->sess_name); rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_TERMINATED; - goto restart; } } else { rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_MAINTAINED; @@ -2618,13 +2595,13 @@ restart: PRINT_INFO("rejected SRP_LOGIN_REQ because the target %s (%s)" " is not enabled", srpt_tgt->scst_tgt->tgt_name, sdev->device->name); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); goto reject; } - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); - ret = srpt_ch_qp_rtr(ch, ch->qp); + ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rtr(ch, ch->qp); if (ret) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); PRINT_ERROR("rejected SRP_LOGIN_REQ because enabling" @@ -2632,8 +2609,8 @@ restart: goto reject; } - TRACE_DBG("Establish connection sess=%p name=%s cm_id=%p", - ch->scst_sess, ch->sess_name, ch->cm_id); + TRACE_DBG("Establish connection sess=%p name=%s ch=%p", + ch->scst_sess, ch->sess_name, ch); /* create srp_login_response */ rsp->opcode = SRP_LOGIN_RSP; @@ -2648,27 +2625,34 @@ restart: ch->req_lim_delta = 0; /* create cm reply */ - rep_param->qp_num = ch->qp->qp_num; - rep_param->private_data = (void *)rsp; - rep_param->private_data_len = sizeof(*rsp); - rep_param->rnr_retry_count = 7; - rep_param->flow_control = 1; - rep_param->failover_accepted = 0; - rep_param->srq = 1; - rep_param->responder_resources = 4; - rep_param->initiator_depth = 4; + if (ch->using_rdma_cm) { + rep_param->rdma_cm.private_data = (void *)rsp; + rep_param->rdma_cm.private_data_len = sizeof(*rsp); + rep_param->rdma_cm.rnr_retry_count = 7; + rep_param->rdma_cm.flow_control = 1; + rep_param->rdma_cm.responder_resources = 4; + rep_param->rdma_cm.initiator_depth = 4; + } else { + rep_param->ib_cm.qp_num = ch->qp->qp_num; + rep_param->ib_cm.private_data = (void *)rsp; + rep_param->ib_cm.private_data_len = sizeof(*rsp); + rep_param->ib_cm.rnr_retry_count = 7; + rep_param->ib_cm.flow_control = 1; + rep_param->ib_cm.failover_accepted = 0; + rep_param->ib_cm.srq = 1; + rep_param->ib_cm.responder_resources = 4; + rep_param->ib_cm.initiator_depth = 4; + } - spin_lock_irq(&srpt_tgt->spinlock); - if (ch->state == CH_CONNECTING) - ret = ib_send_cm_rep(cm_id, rep_param); + if (ch->using_rdma_cm) + ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); else - ret = -ECONNABORTED; - spin_unlock_irq(&srpt_tgt->spinlock); + ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); switch (ret) { case 0: break; - case -ECONNABORTED: + case -EINVAL: goto reject; default: rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); @@ -2691,7 +2675,10 @@ free_ring: ch->max_rsp_size, DMA_TO_DEVICE); free_ch: - cm_id->context = NULL; + if (rdma_cm_id) + rdma_cm_id->context = NULL; + else + ib_cm_id->context = NULL; kfree(ch); ch = NULL; @@ -2703,8 +2690,11 @@ reject: rej->tag = req->tag; rej->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); - ib_send_cm_rej(cm_id, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0, rej, - sizeof(*rej)); + if (rdma_cm_id) + rdma_reject(rdma_cm_id, rej, sizeof(*rej)); + else + ib_send_cm_rej(ib_cm_id, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0, + rej, sizeof(*rej)); if (ch && ch->thread) { srpt_close_ch(ch); @@ -2723,28 +2713,106 @@ out: return ret; } +static int srpt_ib_cm_req_recv(struct ib_cm_id *cm_id, + struct ib_cm_req_event_param *param, + void *private_data) +{ + return srpt_cm_req_recv(cm_id->context, cm_id, NULL, param->port, + param->primary_path->pkey, + private_data); +} + +static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct srpt_device *sdev; + struct srp_login_req req; + const struct srp_login_req_rdma *req_rdma; + + sdev = ib_get_client_data(cm_id->device, &srpt_client); + if (!sdev) + return -ECONNREFUSED; + + if (event->param.conn.private_data_len < sizeof(*req_rdma)) + return -EINVAL; + + /* Transform srp_login_req_rdma into srp_login_req. */ + req_rdma = event->param.conn.private_data; + memset(&req, 0, sizeof(req)); + req.opcode = req_rdma->opcode; + req.tag = req_rdma->tag; + req.req_it_iu_len = req_rdma->req_it_iu_len; + req.req_buf_fmt = req_rdma->req_buf_fmt; + req.req_flags = req_rdma->req_flags; + memcpy(req.initiator_port_id, req_rdma->initiator_port_id, 16); + memcpy(req.target_port_id, req_rdma->target_port_id, 16); + + return srpt_cm_req_recv(sdev, NULL, cm_id, cm_id->port_num, + cm_id->route.path_rec->pkey, &req); +} + static void srpt_cm_rej_recv(struct ib_cm_id *cm_id) { PRINT_INFO("Received InfiniBand REJ packet for cm_id %p.", cm_id); } -/** - * srpt_cm_rtu_recv() - Process IB CM RTU_RECEIVED and USER_ESTABLISHED events. - * - * An IB_CM_RTU_RECEIVED message indicates that the connection is established - * and that the recipient may begin transmitting (RTU = ready to use). - */ -static void srpt_cm_rtu_recv(struct ib_cm_id *cm_id) +static void srpt_check_timeout(struct srpt_rdma_ch *ch) +{ + struct ib_qp_attr attr; + struct ib_qp_init_attr iattr; + uint64_t T_tr_ns, max_compl_time_ms; + uint32_t T_tr_ms; + + if (ib_query_qp(ch->qp, &attr, IB_QP_TIMEOUT, &iattr) < 0) { + PRINT_ERROR("Querying QP attributes failed"); + return; + } + + /* + * From IBTA C9-140: Transport Timer timeout interval + * T_tr = 4.096 us * 2**(local ACK timeout) where the local ACK timeout + * is a five-bit value, with zero meaning that the timer is disabled. + */ + WARN_ON(attr.timeout >= (1 << 5)); + if (attr.timeout) { + T_tr_ns = 1ULL << (12 + attr.timeout); + max_compl_time_ms = attr.retry_cnt * 4 * T_tr_ns; + do_div(max_compl_time_ms, 1000000); + T_tr_ms = T_tr_ns; + do_div(T_tr_ms, 1000000); + TRACE_DBG("Session %s: QP local ack timeout = %d or T_tr =" + " %u ms; retry_cnt = %d; max compl. time = %d ms", + ch->sess_name, attr.timeout, T_tr_ms, + attr.retry_cnt, (unsigned)max_compl_time_ms); + + if (max_compl_time_ms >= RDMA_COMPL_TIMEOUT_S * 1000) { + PRINT_ERROR("Maximum RDMA completion time (%d ms)" + " exceeds ib_srpt timeout (%d ms)", + (unsigned)max_compl_time_ms, + 1000 * RDMA_COMPL_TIMEOUT_S); + } + } +} + +/** + * srpt_cm_rtu_recv() - Process RTU event. + * + * An RTU (read to use) message indicates that the connection has been + * established and that the recipient may begin transmitting. + */ +static void srpt_cm_rtu_recv(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; int ret; - ret = srpt_ch_qp_rts(ch, ch->qp); + ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rts(ch, ch->qp); if (ret < 0) { PRINT_ERROR("%s: QP transition to RTS failed", ch->sess_name); srpt_close_ch(ch); return; } + + srpt_check_timeout(ch); + /* * Note: calling srpt_close_ch() if the transition to the LIVE state * fails is not necessary since that means that that function has @@ -2755,11 +2823,9 @@ static void srpt_cm_rtu_recv(struct ib_cm_id *cm_id) ch->sess_name); } -static void srpt_cm_timewait_exit(struct ib_cm_id *cm_id) +static void srpt_cm_timewait_exit(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; - - PRINT_INFO("Received InfiniBand TimeWait exit for cm_id %p.", cm_id); + PRINT_INFO("Received InfiniBand TimeWait exit for ch %p.", ch); srpt_close_ch(ch); } @@ -2771,28 +2837,18 @@ static void srpt_cm_rep_error(struct ib_cm_id *cm_id) /** * srpt_cm_dreq_recv() - Process reception of a DREQ message. */ -static int srpt_cm_dreq_recv(struct ib_cm_id *cm_id) +static int srpt_cm_dreq_recv(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; - int ret; - - ret = ib_send_cm_drep(cm_id, NULL, 0); - if (ret < 0) - PRINT_ERROR("%s: sending DREP failed", ch->sess_name); - - srpt_close_ch(ch); - - return ret; + srpt_disconnect_ch(ch); + return 0; } /** * srpt_cm_drep_recv() - Process reception of a DREP message. */ -static void srpt_cm_drep_recv(struct ib_cm_id *cm_id) +static void srpt_cm_drep_recv(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; - - PRINT_INFO("Received InfiniBand DREP message for cm_id %p.", cm_id); + PRINT_INFO("Received InfiniBand DREP message for ch %p.", ch); srpt_close_ch(ch); } @@ -2815,24 +2871,24 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) ret = 0; switch (event->event) { case IB_CM_REQ_RECEIVED: - ret = srpt_cm_req_recv(cm_id, &event->param.req_rcvd, - event->private_data); + ret = srpt_ib_cm_req_recv(cm_id, &event->param.req_rcvd, + event->private_data); break; case IB_CM_REJ_RECEIVED: srpt_cm_rej_recv(cm_id); break; case IB_CM_RTU_RECEIVED: case IB_CM_USER_ESTABLISHED: - srpt_cm_rtu_recv(cm_id); + srpt_cm_rtu_recv((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_DREQ_RECEIVED: - ret = srpt_cm_dreq_recv(cm_id); + ret = srpt_cm_dreq_recv((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_DREP_RECEIVED: - srpt_cm_drep_recv(cm_id); + srpt_cm_drep_recv((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_TIMEWAIT_EXIT: - srpt_cm_timewait_exit(cm_id); + srpt_cm_timewait_exit((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_REP_ERROR: srpt_cm_rep_error(cm_id); @@ -2852,6 +2908,37 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) return ret; } +static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + int ret = 0; + + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: + ret = srpt_rdma_cm_req_recv(cm_id, event); + break; + case RDMA_CM_EVENT_ESTABLISHED: + srpt_cm_rtu_recv(cm_id->context); + break; + case RDMA_CM_EVENT_DISCONNECTED: + srpt_cm_dreq_recv(cm_id->context); + break; + case RDMA_CM_EVENT_TIMEWAIT_EXIT: + srpt_cm_timewait_exit(cm_id->context); + break; + case RDMA_CM_EVENT_DEVICE_REMOVAL: + break; + case RDMA_CM_EVENT_ADDR_CHANGE: + break; + default: + PRINT_ERROR("received unrecognized RDMA CM event %d", + event->event); + break; + } + + return ret; +} + /** * srpt_map_sg_to_ib_sge() - Map an SG list to an IB SGE list. */ @@ -3493,7 +3580,8 @@ static int srpt_close_session(struct scst_session *sess) { struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(sess); - ib_send_cm_dreq(ch->cm_id, NULL, 0); + srpt_disconnect_ch(ch); + return 0; } @@ -3502,11 +3590,11 @@ static bool srpt_ch_list_empty(struct srpt_tgt *srpt_tgt) struct srpt_nexus *nexus; bool res = true; - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) if (!list_empty(&nexus->ch_list)) res = false; - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); return res; } @@ -3525,16 +3613,16 @@ static int srpt_release_sport(struct srpt_tgt *srpt_tgt) BUG_ON(!srpt_tgt); /* Disallow new logins and close all active sessions. */ - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); srpt_tgt->enabled = false; __srpt_close_all_ch(srpt_tgt); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); while (wait_event_timeout(srpt_tgt->ch_releaseQ, srpt_ch_list_empty(srpt_tgt), 5 * HZ) <= 0) { PRINT_INFO("%s: waiting for session unregistration ...", srpt_tgt->scst_tgt->tgt_name); - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { list_for_each_entry(ch, &nexus->ch_list, list) { PRINT_INFO("%s: state %s; %d commands in" @@ -3543,15 +3631,15 @@ static int srpt_release_sport(struct srpt_tgt *srpt_tgt) atomic_read(&ch->scst_sess->sess_cmd_count)); } } - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); } - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry_safe(nexus, next_n, &srpt_tgt->nexus_list, entry) { list_del(&nexus->entry); kfree(nexus); } - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); TRACE_EXIT(); return 0; @@ -3799,7 +3887,7 @@ static void srpt_init_tgt(struct srpt_tgt *srpt_tgt) { INIT_LIST_HEAD(&srpt_tgt->nexus_list); init_waitqueue_head(&srpt_tgt->ch_releaseQ); - spin_lock_init(&srpt_tgt->spinlock); + mutex_init(&srpt_tgt->mutex); } /** @@ -3807,6 +3895,7 @@ static void srpt_init_tgt(struct srpt_tgt *srpt_tgt) */ static void srpt_add_one(struct ib_device *device) { + struct ib_cm_id *cm_id; struct srpt_device *sdev; struct srpt_port *sport; struct srpt_tgt *srpt_tgt; @@ -3894,12 +3983,12 @@ static void srpt_add_one(struct ib_device *device) srpt_service_guid = be64_to_cpu(device->node_guid) & ~be64_to_cpu(IB_SERVICE_ID_AGN_MASK); - sdev->cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev); - if (IS_ERR(sdev->cm_id)) { - PRINT_ERROR("ib_create_cm_id() failed: %ld", - PTR_ERR(sdev->cm_id)); + cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev); + if (IS_ERR(cm_id)) { + PRINT_ERROR("ib_create_cm_id() failed: %ld", PTR_ERR(cm_id)); goto err_srq; } + sdev->cm_id = cm_id; /* print out target login information */ TRACE_DBG("Target login info: id_ext=%016llx," @@ -4033,10 +4122,13 @@ static void srpt_remove_one(struct ib_device *device) ib_destroy_cm_id(sdev->cm_id); + ib_set_client_data(device, &srpt_client, NULL); + /* - * SCST target unregistration must happen after destroying sdev->cm_id - * such that no new SRP_LOGIN_REQ information units can arrive while - * unregistering the SCST target. + * SCST target unregistration must happen after sdev->cm_id has been + * destroyed and after the client data has been reset such that no new + * SRP_LOGIN_REQ information units can arrive while unregistering the + * SCST target. */ if (one_target_per_port) { for (i = 0; i < sdev->device->phys_port_cnt; i++) { @@ -4188,20 +4280,55 @@ static int __init srpt_init_module(void) goto out_unregister_target; } + if (rdma_cm_port) { + struct sockaddr_in addr; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + rdma_cm_id = rdma_create_id(srpt_rdma_cm_handler, NULL, + RDMA_PS_TCP, IB_QPT_RC); +#else + rdma_cm_id = rdma_create_id(srpt_rdma_cm_handler, NULL, + RDMA_PS_TCP); +#endif + if (IS_ERR(rdma_cm_id)) { + rdma_cm_id = NULL; + PRINT_ERROR("RDMA/CM ID creation failed"); + goto out_unregister_client; + } + + /* We will listen on any RDMA device. */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = cpu_to_be16(rdma_cm_port); + if (rdma_bind_addr(rdma_cm_id, (void *)&addr)) { + PRINT_ERROR("Binding RDMA/CM ID to port %u failed\n", + rdma_cm_port); + goto out_unregister_client; + } + + if (rdma_listen(rdma_cm_id, 128)) { + PRINT_ERROR("rdma_listen() failed"); + goto out_unregister_client; + } + } + #ifdef CONFIG_SCST_PROC ret = srpt_register_procfs_entry(&srpt_template); if (ret) { PRINT_ERROR("couldn't register procfs entry"); - goto out_unregister_client; + goto out_rdma_cm; } #endif /*CONFIG_SCST_PROC*/ return 0; #ifdef CONFIG_SCST_PROC +out_rdma_cm: + rdma_destroy_id(rdma_cm_id); +#endif /*CONFIG_SCST_PROC*/ out_unregister_client: ib_unregister_client(&srpt_client); -#endif /*CONFIG_SCST_PROC*/ out_unregister_target: scst_unregister_target_template(&srpt_template); out: @@ -4212,6 +4339,7 @@ static void __exit srpt_cleanup_module(void) { TRACE_ENTRY(); + rdma_destroy_id(rdma_cm_id); ib_unregister_client(&srpt_client); #ifdef CONFIG_SCST_PROC srpt_unregister_procfs_entry(&srpt_template); diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 621ca3926..e9b2b4913 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #if defined(INSIDE_KERNEL_TREE) #include @@ -327,15 +328,22 @@ enum rdma_ch_state { * @cmd_wait_list: list of SCST commands that arrived before the RTU event. This * list contains struct srpt_ioctx elements and is protected * against concurrent modification by the cm_id spinlock. - * @pkey_index: P_Key index of the IB partition for this SRP channel. + * @pkey: P_Key of the IB partition for this SRP channel. * @scst_sess: SCST session information associated with this SRP channel. * @sess_name: SCST session name. */ struct srpt_rdma_ch { struct task_struct *thread; struct srpt_nexus *nexus; - struct ib_cm_id *cm_id; struct ib_qp *qp; + union { + struct { + struct ib_cm_id *cm_id; + } ib_cm; + struct { + struct rdma_cm_id *cm_id; + } rdma_cm; + }; struct ib_cq *cq; struct kref kref; int rq_size; @@ -354,7 +362,8 @@ struct srpt_rdma_ch { enum rdma_ch_state state; struct list_head list; struct list_head cmd_wait_list; - uint16_t pkey_index; + uint16_t pkey; + bool using_rdma_cm; bool processing_wait_list; struct scst_session *scst_sess; @@ -364,7 +373,7 @@ struct srpt_rdma_ch { /** * struct srpt_nexus - I_T nexus * @entry: srpt_tgt.nexus_list list node. - * @ch_list: struct srpt_rdma_ch list. Protected by srpt_tgt.spinlock + * @ch_list: struct srpt_rdma_ch list. Protected by srpt_tgt.mutex. * @i_port_id: 128-bit initiator port identifier copied from SRP_LOGIN_REQ. * @t_port_id: 128-bit target port identifier copied from SRP_LOGIN_REQ. */ @@ -378,14 +387,14 @@ struct srpt_nexus { /** * struct srpt_tgt * @ch_releaseQ: Enables waiting for removal from nexus_list. - * @spinlock: Protects nexus_list. + * @mutex: Protects @nexus_list and srpt_nexus.ch_list. * @nexus_list: Per-device I_T nexus list. * @scst_tgt: SCST target information associated with this HCA. * @enabled: Whether or not this SCST target is enabled. */ struct srpt_tgt { wait_queue_head_t ch_releaseQ; - spinlock_t spinlock; + struct mutex mutex; struct list_head nexus_list; struct scst_tgt *scst_tgt; bool enabled; @@ -444,6 +453,25 @@ struct srpt_device { struct srpt_tgt srpt_tgt; }; +/** + * struct srp_login_req_rdma - RDMA/CM login parameters. + * + * RDMA/CM over InfiniBand can only carry 92 - 36 = 56 bytes of private + * data. srp_login_req_rdma contains the same information as + * struct srp_login_req but with the reserved data removed. + * + * To do: Move this structure to . + */ +struct srp_login_req_rdma { + u64 tag; + __be16 req_buf_fmt; + u8 req_flags; + u8 opcode; + __be32 req_it_iu_len; + u8 initiator_port_id[16]; + u8 target_port_id[16]; +}; + #endif /* IB_SRPT_H */ /* diff --git a/usr/fileio/common.c b/usr/fileio/common.c index 96a674144..c756e7736 100644 --- a/usr/fileio/common.c +++ b/usr/fileio/common.c @@ -335,6 +335,7 @@ static int do_exec(struct vdisk_cmd *vcmd) } break; case SYNCHRONIZE_CACHE: + case SYNCHRONIZE_CACHE_16: { int immed = cdb[1] & 0x2; if (data_len == 0)