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)