diff --git a/iscsi-scst/include/iscsi_scst_ver.h b/iscsi-scst/include/iscsi_scst_ver.h index 9b15267ea..a00cef891 100644 --- a/iscsi-scst/include/iscsi_scst_ver.h +++ b/iscsi-scst/include/iscsi_scst_ver.h @@ -13,4 +13,4 @@ * GNU General Public License for more details. */ -#define ISCSI_VERSION_STRING "1.0.2/0.4.17r213" +#define ISCSI_VERSION_STRING "1.0.2/0.4.17r214" diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index caefea89b..0bae263af 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -203,7 +203,9 @@ int __init iscsi_procfs_init(void) goto out; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) proc_iscsi_dir->owner = THIS_MODULE; +#endif err = iscsi_proc_log_entry_build(&iscsi_template); if (err < 0) diff --git a/iscsi-scst/kernel/patches/put_page_callback-2.6.30.patch b/iscsi-scst/kernel/patches/put_page_callback-2.6.30.patch new file mode 100644 index 000000000..a66cb2a9f --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-2.6.30.patch @@ -0,0 +1,378 @@ +diff -upkr linux-2.6.30.1/include/linux/mm_types.h linux-2.6.30.1/include/linux/mm_types.h +--- linux-2.6.30.1/include/linux/mm_types.h 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/include/linux/mm_types.h 2009-07-01 15:20:24.000000000 +0400 +@@ -98,6 +98,18 @@ struct page { + #ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS + unsigned long debug_flags; /* Use atomic bitops on this */ + #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 ++ + }; + + /* +diff -upkr linux-2.6.30.1/include/linux/net.h linux-2.6.30.1/include/linux/net.h +--- linux-2.6.30.1/include/linux/net.h 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/include/linux/net.h 2009-07-01 15:20:24.000000000 +0400 +@@ -57,6 +57,7 @@ typedef enum { + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ ++#include + + struct poll_table_struct; + struct pipe_inode_info; +@@ -356,5 +357,44 @@ static const struct proto_ops name##_ops + extern struct ratelimit_state net_ratelimit_state; + #endif + ++#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 */ +diff -upkr linux-2.6.30.1/net/core/skbuff.c linux-2.6.30.1/net/core/skbuff.c +--- linux-2.6.30.1/net/core/skbuff.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/net/core/skbuff.c 2009-07-01 15:55:08.000000000 +0400 +@@ -75,13 +75,13 @@ static struct kmem_cache *skbuff_fclone_ + static void sock_pipe_buf_release(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { +- put_page(buf->page); ++ net_put_page(buf->page); + } + + static void sock_pipe_buf_get(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { +- get_page(buf->page); ++ net_get_page(buf->page); + } + + static int sock_pipe_buf_steal(struct pipe_inode_info *pipe, +@@ -335,7 +335,7 @@ static void skb_release_data(struct sk_b + if (skb_shinfo(skb)->nr_frags) { + int i; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + } + + if (skb_shinfo(skb)->frag_list) +@@ -750,7 +750,7 @@ struct sk_buff *pskb_copy(struct sk_buff + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; +- get_page(skb_shinfo(n)->frags[i].page); ++ net_get_page(skb_shinfo(n)->frags[i].page); + } + skb_shinfo(n)->nr_frags = i; + } +@@ -816,7 +816,7 @@ int pskb_expand_head(struct sk_buff *skb + sizeof(struct skb_shared_info)); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +- get_page(skb_shinfo(skb)->frags[i].page); ++ net_get_page(skb_shinfo(skb)->frags[i].page); + + if (skb_shinfo(skb)->frag_list) + skb_clone_fraglist(skb); +@@ -1088,7 +1088,7 @@ drop_pages: + skb_shinfo(skb)->nr_frags = i; + + for (; i < nfrags; i++) +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + + if (skb_shinfo(skb)->frag_list) + skb_drop_fraglist(skb); +@@ -1257,7 +1257,7 @@ pull_pages: + k = 0; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + if (skb_shinfo(skb)->frags[i].size <= eat) { +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + eat -= skb_shinfo(skb)->frags[i].size; + } else { + skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; +@@ -1362,7 +1362,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, +@@ -1386,7 +1386,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; + } + +@@ -1396,7 +1396,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; + } +@@ -1417,7 +1417,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; +@@ -2057,7 +2057,7 @@ static inline void skb_split_no_header(s + * where splitting is expensive. + * 2. Split is accurately. We make this. + */ +- get_page(skb_shinfo(skb)->frags[i].page); ++ net_get_page(skb_shinfo(skb)->frags[i].page); + skb_shinfo(skb1)->frags[0].page_offset += len - pos; + skb_shinfo(skb1)->frags[0].size -= len - pos; + skb_shinfo(skb)->frags[i].size = len - pos; +@@ -2179,7 +2179,7 @@ int skb_shift(struct sk_buff *tgt, struc + to++; + + } else { +- get_page(fragfrom->page); ++ net_get_page(fragfrom->page); + fragto->page = fragfrom->page; + fragto->page_offset = fragfrom->page_offset; + fragto->size = todo; +@@ -2201,7 +2201,7 @@ int skb_shift(struct sk_buff *tgt, struc + fragto = &skb_shinfo(tgt)->frags[merge]; + + fragto->size += fragfrom->size; +- put_page(fragfrom->page); ++ net_put_page(fragfrom->page); + } + + /* Reposition in the original skb */ +@@ -2600,7 +2600,7 @@ struct sk_buff *skb_segment(struct sk_bu + + while (pos < offset + len && i < nfrags) { + *frag = skb_shinfo(skb)->frags[i]; +- get_page(frag->page); ++ net_get_page(frag->page); + size = frag->size; + + if (pos < offset) { +diff -upkr linux-2.6.30.1/net/ipv4/ip_output.c linux-2.6.30.1/net/ipv4/ip_output.c +--- linux-2.6.30.1/net/ipv4/ip_output.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/net/ipv4/ip_output.c 2009-07-01 15:55:08.000000000 +0400 +@@ -1018,7 +1018,7 @@ alloc_new_skb: + err = -EMSGSIZE; + goto error; + } +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0); + frag = &skb_shinfo(skb)->frags[i]; + } +@@ -1176,7 +1176,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_shinfo(skb)->frags[i-1].size += 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; +diff -upkr linux-2.6.30.1/net/ipv4/Makefile linux-2.6.30.1/net/ipv4/Makefile +--- linux-2.6.30.1/net/ipv4/Makefile 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/net/ipv4/Makefile 2009-07-01 15:55:08.000000000 +0400 +@@ -49,6 +49,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 +diff -upkr linux-2.6.30.1/net/ipv4/tcp.c linux-2.6.30.1/net/ipv4/tcp.c +--- linux-2.6.30.1/net/ipv4/tcp.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/net/ipv4/tcp.c 2009-07-01 15:55:08.000000000 +0400 +@@ -760,7 +760,7 @@ new_segment: + if (can_coalesce) { + skb_shinfo(skb)->frags[i - 1].size += copy; + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + +@@ -963,7 +963,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; + } +@@ -1004,9 +1004,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; + } + } +diff -upkr linux-2.6.30.1/net/ipv4/tcp_output.c linux-2.6.30.1/net/ipv4/tcp_output.c +--- linux-2.6.30.1/net/ipv4/tcp_output.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/net/ipv4/tcp_output.c 2009-07-01 15:55:08.000000000 +0400 +@@ -889,7 +889,7 @@ static void __pskb_trim_head(struct sk_b + k = 0; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + if (skb_shinfo(skb)->frags[i].size <= eat) { +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + eat -= skb_shinfo(skb)->frags[i].size; + } else { + skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; +diff -upkr linux-2.6.30.1/net/ipv4/tcp_zero_copy.c linux-2.6.30.1/net/ipv4/tcp_zero_copy.c +--- linux-2.6.30.1/net/ipv4/tcp_zero_copy.c 2009-06-16 21:19:51.000000000 +0400 ++++ linux-2.6.30.1/net/ipv4/tcp_zero_copy.c 2009-07-01 15:55:08.000000000 +0400 +@@ -0,0 +1,49 @@ ++/* ++ * 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 ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL(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(net_set_get_put_page_callbacks); +diff -upkr linux-2.6.30.1/net/ipv6/ip6_output.c linux-2.6.30.1/net/ipv6/ip6_output.c +--- linux-2.6.30.1/net/ipv6/ip6_output.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/net/ipv6/ip6_output.c 2009-07-01 15:55:08.000000000 +0400 +@@ -1394,7 +1394,7 @@ alloc_new_skb: + err = -EMSGSIZE; + goto error; + } +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0); + frag = &skb_shinfo(skb)->frags[i]; + } +diff -upkr linux-2.6.30.1/net/Kconfig linux-2.6.30.1/net/Kconfig +--- linux-2.6.30.1/net/Kconfig 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/net/Kconfig 2009-07-01 15:55:08.000000000 +0400 +@@ -52,6 +52,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" diff --git a/scst/README b/scst/README index d7d786feb..ceefa3f0a 100644 --- a/scst/README +++ b/scst/README @@ -70,7 +70,11 @@ scsi_do_req_fifo() or scsi_execute_async_fifo() to be added in the kernel. This patch does that. You may not patch the kernel if you don't need pass-through support. Alternatively, you can define CONFIG_SCST_STRICT_SERIALIZING compile option during the compilation -(see description below). +(see description below). Unfortunately, the CONFIG_SCST_STRICT_SERIALIZING +trick doesn't work on kernels starting from 2.6.30, because those +kernels don't have the required functionality (scsi_execute_async()) +anymore. So, on them to have pass-through working you have to apply +scst_exec_req_fifo-2.6.X.patch. 2. io_context-2.6.X.patch. This patch exports some IO context management functions from the kernel. For performance reasons SCST queues commands @@ -84,9 +88,10 @@ subsystem and greatly improves performance for software RAIDs. See http://sourceforge.net/mailarchive/forum.php?thread_name=a0272b440906030714g67eabc5k8f847fb1e538cc62%40mail.gmail.com&forum_name=scst-devel thread for more details. -4. readahead-context-2.6.X.patch. This is backported version of the -context readahead patch http://lkml.org/lkml/2009/4/12/9, big thanks to -Wu Fengguang! This is a performance improvement patch. +4. readahead-context-2.6.X.patch. This is backported from 2.6.31 version +of the context readahead patch http://lkml.org/lkml/2009/4/12/9, big +thanks to Wu Fengguang. This is a performance improvement patch. It is +included in the mainstream kernel 2.6.31. Then, to compile SCST type 'make scst'. It will build SCST itself and its device handlers. To install them type 'make scst_install'. The driver @@ -643,40 +648,45 @@ CAUTION: If you partitioned/formatted your device with block size X, *NEVER* IMPORTANT: By default for performance reasons VDISK FILEIO devices use write ========= back caching policy. This is generally safe from the consistence of + journaled file systems, laying over them, point of view, but your unsaved cached data will be lost in case of power/hardware/software failure, so you must supply your target server with some kind of UPS or disable write back - caching using WRITE_THROUGH flag. You also should note, that - the file systems journaling over write back caching enabled - devices works reliably *ONLY* if the order of journal writes - is guaranteed or it uses some kind of data protection - barriers (i.e. after writing journal data some kind of - synchronization with media operations is used), otherwise, - because of possible reordering in the cache, even after - successful journal rollback, you very much risk to loose your - data on the FS. Currently, Linux IO subsystem guarantees - order of write operations only using data protection - barriers. Some info about it from the XFS point of view could - be found at http://oss.sgi.com/projects/xfs/faq.html#wcache. - On Linux initiators for EXT3 and ReiserFS file systems the - barrier protection could be turned on using "barrier=1" and + caching using WRITE_THROUGH flag. + Note, that the file systems journaling over write back + caching enabled devices work reliably *ONLY* if the order of + journal writes is guaranteed or they use some kind of data + protection barriers (i.e. after writing journal data some + kind of synchronization with media operations is used), + otherwise, because of possible reordering in the cache, even + after successful journal rollback, you very much risk to + loose your data on the FS. Currently, Linux IO subsystem + guarantees order of write operations only using data + protection barriers. Some info about it from the XFS point of + view could be found at + http://oss.sgi.com/projects/xfs/faq.html#wcache. On Linux + initiators for EXT3 and ReiserFS file systems the barrier + protection could be turned on using "barrier=1" and "barrier=flush" mount options correspondingly. Note, that - usually it's turned off by default (see http://lwn.net/Articles/283161). - You can check if it's turn on or off by looking in /proc/mounts. - Windows and, AFAIK, other UNIX'es don't need any special - explicit options and do necessary barrier actions on - write-back caching devices by default. Also note - that on some real-life workloads write through caching might - perform better, than write back one with the barrier - protection turned on. - Also you should realize that Linux doesn't provide a - guarantee that after sync()/fsync() all written data really - hit permanent storage, they can be then in the cache of your - backstorage device and lost on power failure event. Thus, - ever with write-through cache mode, you still need a good UPS - to protect yourself from your data loss (note, data loss, not - the file system integrity corruption). + usually it's turned off by default (see + http://lwn.net/Articles/283161). You can check if it's turn + on or off by looking in /proc/mounts. Windows and, AFAIK, + other UNIX'es don't need any special explicit options and do + necessary barrier actions on write-back caching devices by + default. Also note that on some real-life workloads write + through caching might perform better, than write back one + with the barrier protection turned on. + Also you should understand that without barriers enabled + (i.e. by default) Linux doesn't provide a guarantee that + after sync()/fsync() all written data really hit permanent + storage. They can be stored in the cache of your backstorage + device only and lost on power failure event. Thus, ever with + write-through cache mode, you still either need to enable + barriers on your backend file system on the target (for + devices in it is, indeed, impossible), or need a good UPS to + protect yourself from your data loss (note, data loss, not + the file system corruption). IMPORTANT: Some disk and partition table management utilities don't support ========= block sizes >512 bytes, therefore make sure that your favorite one diff --git a/scst/README_in-tree b/scst/README_in-tree index cad4413fd..f1afeb968 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -565,40 +565,45 @@ CAUTION: If you partitioned/formatted your device with block size X, *NEVER* IMPORTANT: By default for performance reasons VDISK FILEIO devices use write ========= back caching policy. This is generally safe from the consistence of + journaled file systems, laying over them, point of view, but your unsaved cached data will be lost in case of power/hardware/software failure, so you must supply your target server with some kind of UPS or disable write back - caching using WRITE_THROUGH flag. You also should note, that - the file systems journaling over write back caching enabled - devices works reliably *ONLY* if the order of journal writes - is guaranteed or it uses some kind of data protection - barriers (i.e. after writing journal data some kind of - synchronization with media operations is used), otherwise, - because of possible reordering in the cache, even after - successful journal rollback, you very much risk to loose your - data on the FS. Currently, Linux IO subsystem guarantees - order of write operations only using data protection - barriers. Some info about it from the XFS point of view could - be found at http://oss.sgi.com/projects/xfs/faq.html#wcache. - On Linux initiators for EXT3 and ReiserFS file systems the - barrier protection could be turned on using "barrier=1" and + caching using WRITE_THROUGH flag. + Note, that the file systems journaling over write back + caching enabled devices work reliably *ONLY* if the order of + journal writes is guaranteed or they use some kind of data + protection barriers (i.e. after writing journal data some + kind of synchronization with media operations is used), + otherwise, because of possible reordering in the cache, even + after successful journal rollback, you very much risk to + loose your data on the FS. Currently, Linux IO subsystem + guarantees order of write operations only using data + protection barriers. Some info about it from the XFS point of + view could be found at + http://oss.sgi.com/projects/xfs/faq.html#wcache. On Linux + initiators for EXT3 and ReiserFS file systems the barrier + protection could be turned on using "barrier=1" and "barrier=flush" mount options correspondingly. Note, that - usually it's turned off by default (see http://lwn.net/Articles/283161). - You can check if it's turn on or off by looking in /proc/mounts. - Windows and, AFAIK, other UNIX'es don't need any special - explicit options and do necessary barrier actions on - write-back caching devices by default. Also note - that on some real-life workloads write through caching might - perform better, than write back one with the barrier - protection turned on. - Also you should realize that Linux doesn't provide a - guarantee that after sync()/fsync() all written data really - hit permanent storage, they can be then in the cache of your - backstorage device and lost on power failure event. Thus, - ever with write-through cache mode, you still need a good UPS - to protect yourself from your data loss (note, data loss, not - the file system integrity corruption). + usually it's turned off by default (see + http://lwn.net/Articles/283161). You can check if it's turn + on or off by looking in /proc/mounts. Windows and, AFAIK, + other UNIX'es don't need any special explicit options and do + necessary barrier actions on write-back caching devices by + default. Also note that on some real-life workloads write + through caching might perform better, than write back one + with the barrier protection turned on. + Also you should understand that without barriers enabled + (i.e. by default) Linux doesn't provide a guarantee that + after sync()/fsync() all written data really hit permanent + storage. They can be stored in the cache of your backstorage + device only and lost on power failure event. Thus, ever with + write-through cache mode, you still either need to enable + barriers on your backend file system on the target (for + devices in it is, indeed, impossible), or need a good UPS to + protect yourself from your data loss (note, data loss, not + the file system corruption). IMPORTANT: Some disk and partition table management utilities don't support ========= block sizes >512 bytes, therefore make sure that your favorite one diff --git a/scst/include/scst.h b/scst/include/scst.h index d6c0bc901..f30b62a9a 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2719,6 +2719,12 @@ static inline void sg_set_page(struct scatterlist *sg, struct page *page, sg->length = len; } +static inline struct scatterlist *sg_next(struct scatterlist *sg) +{ + sg++; + return sg; +} + #endif #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) */ diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-2.6.30.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-2.6.30.patch new file mode 100644 index 000000000..b6419e2fd --- /dev/null +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-2.6.30.patch @@ -0,0 +1,12 @@ +diff -upkr -X linux-2.6.30/Documentation/dontdiff linux-2.6.30/drivers/Kconfig linux-2.6.30/drivers/Kconfig +--- linux-2.6.30/drivers/Kconfig 2008-07-14 01:51:29.000000000 +0400 ++++ linux-2.6.30/drivers/Kconfig 2008-07-24 14:14:46.000000000 +0400 +@@ -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.drivers.Linux-2.6.30.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-2.6.30.patch new file mode 100644 index 000000000..b26ddd2e6 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-2.6.30.patch @@ -0,0 +1,11 @@ +diff -upkr -X linux-2.6.30/Documentation/dontdiff linux-2.6.30/drivers/Makefile linux-2.6.30/drivers/Makefile +--- linux-2.6.30/drivers/Makefile 2008-07-14 01:51:29.000000000 +0400 ++++ linux-2.6.30/drivers/Makefile 2008-07-24 14:15:29.000000000 +0400 +@@ -41,6 +41,7 @@ obj-$(CONFIG_ATM) += atm/ + obj-y += macintosh/ + obj-$(CONFIG_IDE) += ide/ + obj-$(CONFIG_SCSI) += scsi/ ++obj-$(CONFIG_SCST) += scst/ + obj-$(CONFIG_ATA) += ata/ + obj-y += net/ + obj-$(CONFIG_ATM) += atm/ diff --git a/scst/kernel/io_context-2.6.30.patch b/scst/kernel/io_context-2.6.30.patch new file mode 100644 index 000000000..7946ea3aa --- /dev/null +++ b/scst/kernel/io_context-2.6.30.patch @@ -0,0 +1,61 @@ +diff -upkr linux-2.6.30.1/block/blk-ioc.c linux-2.6.30.1/block/blk-ioc.c +--- linux-2.6.30.1/block/blk-ioc.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/block/blk-ioc.c 2009-07-01 15:55:08.000000000 +0400 +@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context * + rcu_read_unlock(); + } + ++void __exit_io_context(struct io_context *ioc) ++{ ++ if (ioc == NULL) ++ return; ++ ++ if (atomic_dec_and_test(&ioc->nr_tasks)) { ++ if (ioc->aic && ioc->aic->exit) ++ ioc->aic->exit(ioc->aic); ++ cfq_exit(ioc); ++ ++ put_io_context(ioc); ++ } ++} ++EXPORT_SYMBOL(__exit_io_context); ++ + /* Called by the exitting task */ + void exit_io_context(void) + { +@@ -75,13 +90,7 @@ void exit_io_context(void) + current->io_context = NULL; + task_unlock(current); + +- if (atomic_dec_and_test(&ioc->nr_tasks)) { +- if (ioc->aic && ioc->aic->exit) +- ioc->aic->exit(ioc->aic); +- cfq_exit(ioc); +- +- put_io_context(ioc); +- } ++ __exit_io_context(ioc); + } + + struct io_context *alloc_io_context(gfp_t gfp_flags, int node) +@@ -105,6 +114,7 @@ struct io_context *alloc_io_context(gfp_ + + return ret; + } ++EXPORT_SYMBOL(alloc_io_context); + + /* + * If the current task has no IO context then create one and initialise it. +diff -upkr linux-2.6.30.1/include/linux/iocontext.h linux-2.6.30.1/include/linux/iocontext.h +--- linux-2.6.30.1/include/linux/iocontext.h 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/include/linux/iocontext.h 2009-07-01 15:20:24.000000000 +0400 +@@ -103,7 +103,9 @@ static inline struct io_context *ioc_tas + int put_io_context(struct io_context *ioc); + void exit_io_context(void); + struct io_context *get_io_context(gfp_t gfp_flags, int node); ++#define SCST_IO_CONTEXT + struct io_context *alloc_io_context(gfp_t gfp_flags, int node); ++void __exit_io_context(struct io_context *ioc); + void copy_io_context(struct io_context **pdst, struct io_context **psrc); + #else + static inline void exit_io_context(void) diff --git a/scst/kernel/readahead-2.6.30.patch b/scst/kernel/readahead-2.6.30.patch new file mode 100644 index 000000000..64db41c1e --- /dev/null +++ b/scst/kernel/readahead-2.6.30.patch @@ -0,0 +1,12 @@ +diff -upkr linux-2.6.30.1/mm/readahead.c linux-2.6.30.1/mm/readahead.c +--- linux-2.6.30.1/mm/readahead.c 2009-07-01 13:52:31.000000000 +0400 ++++ linux-2.6.30.1/mm/readahead.c 2009-07-01 15:55:08.000000000 +0400 +@@ -565,5 +565,8 @@ page_cache_async_readahead(struct addres + + /* do read-ahead */ + ondemand_readahead(mapping, ra, filp, true, offset, req_size); ++ ++ if (PageUptodate(page)) ++ blk_run_backing_dev(mapping->backing_dev_info, NULL); + } + EXPORT_SYMBOL_GPL(page_cache_async_readahead); diff --git a/scst/kernel/scst_exec_req_fifo-2.6.30.patch b/scst/kernel/scst_exec_req_fifo-2.6.30.patch new file mode 100644 index 000000000..43108ef71 --- /dev/null +++ b/scst/kernel/scst_exec_req_fifo-2.6.30.patch @@ -0,0 +1,789 @@ +diff -upkr linux-2.6.30.1/block/blk-map.c linux-2.6.30.1/block/blk-map.c +--- linux-2.6.30.1/block/blk-map.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/block/blk-map.c 2009-07-08 21:18:53.000000000 +0400 +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include /* for struct sg_iovec */ + + #include "blk.h" +@@ -154,7 +155,7 @@ int blk_rq_map_user(struct request_queue + } + + if (!bio_flagged(bio, BIO_USER_MAPPED)) +- rq->cmd_flags |= REQ_COPY_USER; ++ rq->cmd_flags |= REQ_HAS_TAIL_SPACE_FOR_PADDING; + + rq->buffer = rq->data = NULL; + return 0; +@@ -230,7 +231,7 @@ int blk_rq_map_user_iov(struct request_q + } + + if (!bio_flagged(bio, BIO_USER_MAPPED)) +- rq->cmd_flags |= REQ_COPY_USER; ++ rq->cmd_flags |= REQ_HAS_TAIL_SPACE_FOR_PADDING; + + blk_queue_bounce(q, &bio); + bio_get(bio); +@@ -273,6 +274,532 @@ int blk_rq_unmap_user(struct bio *bio) + EXPORT_SYMBOL(blk_rq_unmap_user); + + /** ++ * blk_copy_sg - copy one SG vector to another ++ * @dst_sg: destination SG ++ * @src_sg: source SG ++ * @copy_len: maximum amount of data to copy. If 0, then copy all. ++ * @d_km_type: kmap_atomic type for the destination SG ++ * @s_km_type: kmap_atomic type for the source SG ++ * ++ * Description: ++ * Data from the destination SG vector will be copied to the source SG ++ * vector. End of the vectors will be determined by sg_next() returning ++ * NULL. Returns number of bytes copied. ++ */ ++int blk_copy_sg(struct scatterlist *dst_sg, ++ struct scatterlist *src_sg, size_t copy_len, ++ enum km_type d_km_type, enum km_type s_km_type) ++{ ++ int res = 0; ++ size_t src_len, dst_len, src_offs, dst_offs; ++ struct page *src_page, *dst_page; ++ ++ if (copy_len == 0) ++ copy_len = 0x7FFFFFFF; /* copy all */ ++ ++ dst_page = sg_page(dst_sg); ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ ++ src_offs = 0; ++ do { ++ 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, s_km_type) + src_offs; ++ daddr = kmap_atomic(dst_page, d_km_type) + dst_offs; ++ ++ if ((src_offs == 0) && (dst_offs == 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_SIZE - src_offs); ++ 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, s_km_type); ++ kunmap_atomic(daddr, d_km_type); ++ ++ res += n; ++ copy_len -= n; ++ if (copy_len == 0) ++ goto out; ++ ++ if ((src_offs & ~PAGE_MASK) == 0) { ++ src_page = nth_page(src_page, 1); ++ src_offs = 0; ++ } ++ if ((dst_offs & ~PAGE_MASK) == 0) { ++ dst_page = nth_page(dst_page, 1); ++ dst_offs = 0; ++ } ++ ++ 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); ++ ++ src_sg = sg_next(src_sg); ++ } while (src_sg != NULL); ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(blk_copy_sg); ++ ++void blk_rq_unmap_kern_sg(struct request *req, int do_copy) ++{ ++ struct scatterlist *hdr = (struct scatterlist *)req->end_io_data; ++ ++ if (hdr == NULL) ++ goto out; ++ ++ if (hdr->length == 0) { ++ /* Tail element only was copied */ ++ struct scatterlist *new_sg = &hdr[1]; ++ struct scatterlist *orig_sg = (struct scatterlist *)hdr->page_link; ++ ++ if ((rq_data_dir(req) == READ) && do_copy) { ++ void *saddr, *daddr; ++ ++ saddr = kmap_atomic(sg_page(orig_sg), KM_BIO_SRC_IRQ); ++ daddr = kmap_atomic(sg_page(new_sg), KM_BIO_DST_IRQ) + ++ new_sg->offset; ++ memcpy(daddr, saddr, orig_sg->length); ++ kunmap_atomic(saddr, KM_BIO_SRC_IRQ); ++ kunmap_atomic(daddr, KM_BIO_DST_IRQ); ++ } ++ ++ __free_pages(sg_page(orig_sg), get_order(orig_sg->length)); ++ *orig_sg = *new_sg; ++ kfree(hdr); ++ } else { ++ /* The whole SG was copied */ ++ struct scatterlist *new_sgl = &hdr[1]; ++ struct scatterlist *orig_sgl = (struct scatterlist *)hdr->page_link; ++ struct scatterlist *sg, *start_sg; ++ int n; ++ ++ if ((rq_data_dir(req) == READ) && do_copy) { ++ blk_copy_sg(orig_sgl, new_sgl, 0, KM_BIO_DST_IRQ, ++ KM_BIO_SRC_IRQ); ++ } ++ ++ start_sg = hdr; ++ sg = new_sgl; ++ n = 1; ++ while (sg != NULL) { ++ __free_page(sg_page(sg)); ++ sg = sg_next(sg); ++ n++; ++ /* One entry for chaining */ ++ if ((sg == NULL) || (n == (SG_MAX_SINGLE_ALLOC - 1))) { ++ kfree(start_sg); ++ start_sg = sg; ++ n = 0; ++ } ++ } ++ } ++ ++out: ++ return; ++} ++ ++static int blk_rq_handle_align_tail_only(struct request *rq, ++ struct scatterlist *sg_to_copy, ++ gfp_t gfp, gfp_t page_gfp) ++{ ++ int res = 0; ++ struct scatterlist *tail_sg = sg_to_copy; ++ struct scatterlist *new_sg; ++ struct scatterlist *hdr; ++ int new_sg_nents; ++ struct page *pg; ++ ++ new_sg_nents = 2; ++ ++ new_sg = kmalloc(sizeof(*new_sg) * new_sg_nents, gfp); ++ if (new_sg == NULL) ++ goto out_nomem; ++ ++ sg_init_table(new_sg, new_sg_nents); ++ ++ hdr = new_sg; ++ new_sg++; ++ new_sg_nents--; ++ ++ hdr->page_link = (unsigned long)tail_sg; ++ *new_sg = *tail_sg; ++ ++ pg = alloc_pages(page_gfp, get_order(tail_sg->length)); ++ if (pg == NULL) ++ goto err_free_new_sg; ++ ++ if (rq_data_dir(rq) == WRITE) { ++ void *saddr, *daddr; ++ saddr = kmap_atomic(sg_page(tail_sg), KM_USER0) + ++ tail_sg->offset; ++ daddr = kmap_atomic(pg, KM_USER1); ++ memcpy(daddr, saddr, tail_sg->length); ++ kunmap_atomic(saddr, KM_USER0); ++ kunmap_atomic(daddr, KM_USER1); ++ } ++ ++ sg_assign_page(tail_sg, pg); ++ tail_sg->offset = 0; ++ ++ rq->end_io_data = hdr; ++ rq->cmd_flags |= REQ_HAS_TAIL_SPACE_FOR_PADDING; ++ ++out: ++ return res; ++ ++err_free_new_sg: ++ kfree(new_sg); ++ ++out_nomem: ++ res = -ENOMEM; ++ goto out; ++} ++ ++static int blk_rq_handle_align(struct request *rq, struct scatterlist **psgl, ++ int *pnents, struct scatterlist *sgl_to_copy, ++ int nents_to_copy, gfp_t gfp, gfp_t page_gfp) ++{ ++ int res = 0, i; ++ struct scatterlist *sgl = *psgl; ++ int nents = *pnents; ++ struct scatterlist *sg, *prev_sg; ++ struct scatterlist *new_sgl; ++ struct scatterlist *hdr; ++ size_t len = 0, to_copy; ++ int new_sgl_nents, new_sgl_nents_to_alloc, n; ++ ++ if (sgl != sgl_to_copy) { ++ /* Copy only the last element */ ++ res = blk_rq_handle_align_tail_only(rq, sgl_to_copy, ++ gfp, page_gfp); ++ if (res == 0) ++ goto out; ++ } ++ ++ for_each_sg(sgl, sg, nents, i) ++ len += sg->length; ++ to_copy = len; ++ ++ /* ++ * Let's keep each SG allocation inside a single page to decrease ++ * probability of failure. ++ */ ++ ++ new_sgl_nents = PFN_UP(len) + 1; ++ new_sgl_nents_to_alloc = new_sgl_nents + ++ ((new_sgl_nents - 1) / SG_MAX_SINGLE_ALLOC); ++ n = min_t(size_t, SG_MAX_SINGLE_ALLOC, new_sgl_nents_to_alloc); ++ ++ new_sgl = kmalloc(sizeof(*new_sgl) * n, gfp); ++ if (new_sgl == NULL) ++ goto out_nomem; ++ ++ sg_init_table(new_sgl, n); ++ ++ new_sgl_nents_to_alloc -= n; ++ sg = new_sgl; ++ while (new_sgl_nents_to_alloc > 0) { ++ prev_sg = sg; ++ n = min_t(size_t, SG_MAX_SINGLE_ALLOC, new_sgl_nents_to_alloc); ++ ++ sg = kmalloc(sizeof(*sg) * n, gfp); ++ if (sg == NULL) ++ goto out_nomem; ++ ++ sg_init_table(sg, n); ++ sg_chain(prev_sg, SG_MAX_SINGLE_ALLOC, sg); ++ ++ new_sgl_nents_to_alloc -= n; ++ }; ++ ++ hdr = new_sgl; ++ new_sgl++; ++ new_sgl_nents--; ++ ++ hdr->page_link = (unsigned long)sgl; ++ hdr->length = nents; ++ ++ for_each_sg(new_sgl, sg, new_sgl_nents, i) { ++ struct page *pg; ++ ++ pg = alloc_page(page_gfp); ++ if (pg == NULL) ++ goto err_free_new_sgl; ++ ++ 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 not marked as last in ++ * SG chaining. ++ */ ++ blk_copy_sg(new_sgl, sgl, to_copy, KM_USER0, KM_USER1); ++ } ++ ++ rq->end_io_data = hdr; ++ rq->cmd_flags |= REQ_HAS_TAIL_SPACE_FOR_PADDING; ++ ++ *psgl = new_sgl; ++ *pnents = new_sgl_nents; ++ ++out: ++ return res; ++ ++err_free_new_sgl: ++ for_each_sg(new_sgl, sg, new_sgl_nents, i) { ++ struct page *pg = sg_page(sg); ++ if (pg == NULL) ++ break; ++ __free_page(pg); ++ } ++ ++out_nomem: ++ res = -ENOMEM; ++ goto out; ++} ++ ++static void bio_map_kern_endio(struct bio *bio, int err) ++{ ++ bio_put(bio); ++} ++ ++static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp, struct scatterlist **sgl_to_copy, ++ int *nents_to_copy) ++{ ++ 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; ++ ++ *sgl_to_copy = NULL; ++ ++ if (unlikely((sgl == 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; ++ 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_need_copy; ++ } ++ ++ 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 |= 1 << BIO_RW; ++ ++ bio->bi_end_io = 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_need_copy; ++ } 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_HAS_TAIL_SPACE_FOR_PADDING)) { ++ res = -EINVAL; ++ if (sgl->offset == 0) { ++ *sgl_to_copy = prev_sg; ++ *nents_to_copy = 1; ++ goto out_free_bios; ++ } else ++ goto out_need_copy; ++ } ++ ++ 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; ++ goto out_free_bios; ++ } ++ } ++ ++ rq->buffer = rq->data = NULL; ++ ++out: ++ return res; ++ ++out_need_copy: ++ *sgl_to_copy = sgl; ++ *nents_to_copy = nents; ++ ++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; ++ struct scatterlist *sg_to_copy = NULL; ++ int nents_to_copy = 0; ++ ++ if (unlikely((sgl == 0) || (sgl->length == 0) || ++ (nents <= 0) || (rq->end_io_data != NULL))) { ++ WARN_ON(1); ++ res = -EINVAL; ++ goto out; ++ } ++ ++ res = __blk_rq_map_kern_sg(rq, sgl, nents, gfp, &sg_to_copy, ++ &nents_to_copy); ++ if (unlikely(res != 0)) { ++ if (sg_to_copy == NULL) ++ goto out; ++ ++ res = blk_rq_handle_align(rq, &sgl, &nents, sg_to_copy, ++ nents_to_copy, gfp, rq->q->bounce_gfp | gfp); ++ if (unlikely(res != 0)) ++ goto out; ++ ++ res = __blk_rq_map_kern_sg(rq, sgl, nents, gfp, &sg_to_copy, ++ &nents_to_copy); ++ if (res != 0) { ++ blk_rq_unmap_kern_sg(rq, 0); ++ goto out; ++ } ++ } ++ ++ rq->buffer = rq->data = NULL; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(blk_rq_map_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 + * @rq: request to fill +@@ -309,7 +836,7 @@ int blk_rq_map_kern(struct request_queue + bio->bi_rw |= (1 << BIO_RW); + + if (do_copy) +- rq->cmd_flags |= REQ_COPY_USER; ++ rq->cmd_flags |= REQ_HAS_TAIL_SPACE_FOR_PADDING; + + blk_rq_bio_prep(q, rq, bio); + blk_queue_bounce(q, &rq->bio); +diff -upkr linux-2.6.30.1/block/blk-merge.c linux-2.6.30.1/block/blk-merge.c +--- linux-2.6.30.1/block/blk-merge.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/block/blk-merge.c 2009-07-08 21:18:53.000000000 +0400 +@@ -198,7 +198,7 @@ new_segment: + } /* segments in rq */ + + +- if (unlikely(rq->cmd_flags & REQ_COPY_USER) && ++ if ((rq->cmd_flags & REQ_HAS_TAIL_SPACE_FOR_PADDING) && + (rq->data_len & q->dma_pad_mask)) { + unsigned int pad_len = (q->dma_pad_mask & ~rq->data_len) + 1; + +diff -upkr linux-2.6.30.1/drivers/scsi/scsi_lib.c linux-2.6.30.1/drivers/scsi/scsi_lib.c +--- linux-2.6.30.1/drivers/scsi/scsi_lib.c 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/drivers/scsi/scsi_lib.c 2009-07-08 21:18:53.000000000 +0400 +@@ -277,6 +277,100 @@ int scsi_execute_req(struct scsi_device + } + EXPORT_SYMBOL(scsi_execute_req); + ++struct scsi_io_context { ++ void *blk_data; ++ void *data; ++ void (*done)(void *data, char *sense, int result, int resid); ++ char sense[SCSI_SENSE_BUFFERSIZE]; ++}; ++ ++static struct kmem_cache *scsi_io_context_cache; ++ ++static void scsi_end_async(struct request *req, int error) ++{ ++ struct scsi_io_context *sioc = req->end_io_data; ++ ++ req->end_io_data = sioc->blk_data; ++ blk_rq_unmap_kern_sg(req, (error == 0)); ++ ++ if (sioc->done) ++ sioc->done(sioc->data, sioc->sense, req->errors, req->data_len); ++ ++ kmem_cache_free(scsi_io_context_cache, sioc); ++ __blk_put_request(req->q, req); ++} ++ ++/** ++ * scsi_execute_async - insert request ++ * @sdev: scsi device ++ * @cmd: scsi command ++ * @cmd_len: length of scsi cdb ++ * @data_direction: DMA_TO_DEVICE, DMA_FROM_DEVICE, or DMA_NONE ++ * @sgl: data buffer scatterlist ++ * @nents: number of elements in the sgl ++ * @timeout: request timeout in seconds ++ * @retries: number of times to retry request ++ * @privdata: data passed to done() ++ * @done: callback function when done ++ * @gfp: memory allocation flags ++ * @flags: one or more SCSI_ASYNC_EXEC_FLAG_* flags ++ */ ++int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd, ++ int cmd_len, int data_direction, struct scatterlist *sgl, ++ int nents, int timeout, int retries, void *privdata, ++ void (*done)(void *, char *, int, int), gfp_t gfp, ++ int flags) ++{ ++ struct request *req; ++ struct scsi_io_context *sioc; ++ int err = 0; ++ int write = (data_direction == DMA_TO_DEVICE); ++ ++ sioc = kmem_cache_zalloc(scsi_io_context_cache, gfp); ++ if (sioc == NULL) ++ return DRIVER_ERROR << 24; ++ ++ req = blk_get_request(sdev->request_queue, write, gfp); ++ if (req == NULL) ++ goto free_sense; ++ req->cmd_type = REQ_TYPE_BLOCK_PC; ++ req->cmd_flags |= REQ_QUIET; ++ ++ if (flags & SCSI_ASYNC_EXEC_FLAG_HAS_TAIL_SPACE_FOR_PADDING) ++ req->cmd_flags |= REQ_HAS_TAIL_SPACE_FOR_PADDING; ++ ++ if (sgl != NULL) { ++ err = blk_rq_map_kern_sg(req, sgl, nents, gfp); ++ if (err) ++ goto free_req; ++ } ++ ++ sioc->blk_data = req->end_io_data; ++ sioc->data = privdata; ++ sioc->done = done; ++ ++ req->cmd_len = cmd_len; ++ memset(req->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */ ++ memcpy(req->cmd, cmd, req->cmd_len); ++ req->sense = sioc->sense; ++ req->sense_len = 0; ++ req->timeout = timeout; ++ req->retries = retries; ++ req->end_io_data = sioc; ++ ++ blk_execute_rq_nowait(req->q, NULL, req, ++ flags & SCSI_ASYNC_EXEC_FLAG_AT_HEAD, scsi_end_async); ++ return 0; ++ ++free_req: ++ blk_put_request(req); ++ ++free_sense: ++ kmem_cache_free(scsi_io_context_cache, sioc); ++ return DRIVER_ERROR << 24; ++} ++EXPORT_SYMBOL_GPL(scsi_execute_async); ++ + /* + * Function: scsi_init_cmd_errh() + * +@@ -1743,12 +1837,20 @@ int __init scsi_init_queue(void) + { + int i; + ++ scsi_io_context_cache = kmem_cache_create("scsi_io_context", ++ sizeof(struct scsi_io_context), ++ 0, 0, NULL); ++ if (!scsi_io_context_cache) { ++ printk(KERN_ERR "SCSI: can't init scsi io context cache\n"); ++ return -ENOMEM; ++ } ++ + scsi_sdb_cache = kmem_cache_create("scsi_data_buffer", + sizeof(struct scsi_data_buffer), + 0, 0, NULL); + if (!scsi_sdb_cache) { + printk(KERN_ERR "SCSI: can't init scsi sdb cache\n"); +- return -ENOMEM; ++ goto cleanup_io_context; + } + + for (i = 0; i < SG_MEMPOOL_NR; i++) { +@@ -1784,6 +1886,9 @@ cleanup_sdb: + } + kmem_cache_destroy(scsi_sdb_cache); + ++cleanup_io_context: ++ kmem_cache_destroy(scsi_io_context_cache); ++ + return -ENOMEM; + } + +@@ -1791,6 +1896,7 @@ void scsi_exit_queue(void) + { + int i; + ++ kmem_cache_destroy(scsi_io_context_cache); + kmem_cache_destroy(scsi_sdb_cache); + + for (i = 0; i < SG_MEMPOOL_NR; i++) { +diff -upkr linux-2.6.30.1/include/linux/blkdev.h linux-2.6.30.1/include/linux/blkdev.h +--- linux-2.6.30.1/include/linux/blkdev.h 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/include/linux/blkdev.h 2009-07-08 21:18:53.000000000 +0400 +@@ -115,7 +115,7 @@ enum rq_flag_bits { + __REQ_RW_SYNC, /* request is sync (sync write or read) */ + __REQ_ALLOCED, /* request came from our alloc pool */ + __REQ_RW_META, /* metadata io request */ +- __REQ_COPY_USER, /* contains copies of user pages */ ++ __REQ_HAS_TAIL_SPACE_FOR_PADDING, /* has space for padding in the tail */ + __REQ_INTEGRITY, /* integrity metadata has been remapped */ + __REQ_NOIDLE, /* Don't anticipate more IO after this one */ + __REQ_IO_STAT, /* account I/O stat */ +@@ -143,7 +143,7 @@ enum rq_flag_bits { + #define REQ_RW_SYNC (1 << __REQ_RW_SYNC) + #define REQ_ALLOCED (1 << __REQ_ALLOCED) + #define REQ_RW_META (1 << __REQ_RW_META) +-#define REQ_COPY_USER (1 << __REQ_COPY_USER) ++#define REQ_HAS_TAIL_SPACE_FOR_PADDING (1 << __REQ_HAS_TAIL_SPACE_FOR_PADDING) + #define REQ_INTEGRITY (1 << __REQ_INTEGRITY) + #define REQ_NOIDLE (1 << __REQ_NOIDLE) + #define REQ_IO_STAT (1 << __REQ_IO_STAT) +@@ -807,6 +807,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 *req, int do_copy); + extern int blk_execute_rq(struct request_queue *, struct gendisk *, + struct request *, int); + extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, +@@ -909,6 +912,9 @@ extern void blk_dump_rq_flags(struct req + extern void generic_unplug_device(struct request_queue *); + extern long nr_blockdev_pages(void); + ++extern int blk_copy_sg(struct scatterlist *, struct scatterlist *, size_t, ++ enum km_type, enum km_type); ++ + int blk_get_queue(struct request_queue *); + struct request_queue *blk_alloc_queue(gfp_t); + struct request_queue *blk_alloc_queue_node(gfp_t, int); +diff -upkr linux-2.6.30.1/include/scsi/scsi_device.h linux-2.6.30.1/include/scsi/scsi_device.h +--- linux-2.6.30.1/include/scsi/scsi_device.h 2009-06-10 07:05:27.000000000 +0400 ++++ linux-2.6.30.1/include/scsi/scsi_device.h 2009-07-06 20:24:54.000000000 +0400 +@@ -372,6 +372,17 @@ extern int scsi_execute_req(struct scsi_ + struct scsi_sense_hdr *, int timeout, int retries, + int *resid); + ++#define SCSI_ASYNC_EXEC_FLAG_AT_HEAD 1 ++#define SCSI_ASYNC_EXEC_FLAG_HAS_TAIL_SPACE_FOR_PADDING 2 ++ ++#define SCSI_EXEC_REQ_FIFO_DEFINED ++extern int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd, ++ int cmd_len, int data_direction, ++ struct scatterlist *sgl, int nents, int timeout, ++ int retries, void *privdata, ++ void (*done)(void *, char *, int, int), ++ gfp_t gfp, int flags); ++ + static inline int __must_check scsi_device_reprobe(struct scsi_device *sdev) + { + return device_reprobe(&sdev->sdev_gendev); diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 52e18111f..369a49e64 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -2476,7 +2476,11 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr, struct page *page = virt_to_page(addr); if (need_new_bio) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + bio = bio_kmalloc(GFP_KERNEL, max_nr_vecs); +#else bio = bio_alloc(GFP_KERNEL, max_nr_vecs); +#endif if (!bio) { PRINT_ERROR("Failed to create bio " "for data segment= %d cmd %p", @@ -3449,7 +3453,7 @@ static int vcdrom_write_proc(char *buffer, char **start, off_t offset, if (!strncmp("close ", p, 6)) { p += 6; action = 0; - } else if (!strncmp("change ", p, 5)) { + } else if (!strncmp("change ", p, 7)) { p += 7; action = 1; } else if (!strncmp("open ", p, 5)) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 1a3424cfa..ddc6f059e 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "scst.h" #include "scst_priv.h" @@ -2258,6 +2259,15 @@ void scst_release_request(struct scst_cmd *cmd) } #endif +static bool is_report_sg_limitation(void) +{ +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + return (trace_flag & TRACE_OUT_OF_MEM); +#else + return false; +#endif +} + int scst_alloc_space(struct scst_cmd *cmd) { gfp_t gfp_mask; @@ -2281,7 +2291,7 @@ int scst_alloc_space(struct scst_cmd *cmd) goto out; if (unlikely(cmd->sg_cnt > tgt_dev->max_sg_cnt)) { - if (ll < 10) { + if ((ll < 10) || is_report_sg_limitation()) { PRINT_INFO("Unable to complete command due to " "SG IO count limitation (requested %d, " "available %d, tgt lim %d)", cmd->sg_cnt, @@ -2301,7 +2311,7 @@ int scst_alloc_space(struct scst_cmd *cmd) goto out_sg_free; if (unlikely(cmd->in_sg_cnt > tgt_dev->max_sg_cnt)) { - if (ll < 10) { + if ((ll < 10) || is_report_sg_limitation()) { PRINT_INFO("Unable to complete command due to " "SG IO count limitation (IN buffer, requested " "%d, available %d, tgt lim %d)", cmd->in_sg_cnt, @@ -2364,38 +2374,128 @@ out: return; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) || !defined(SCSI_EXEC_REQ_FIFO_DEFINED) +/** + * blk_copy_sg - copy one SG vector to another + * @dst_sg: destination SG + * @src_sg: source SG + * @copy_len: maximum amount of data to copy. If 0, then copy all. + * @d_km_type: kmap_atomic type for the destination SG + * @s_km_type: kmap_atomic type for the source SG + * + * Description: + * Data from the destination SG vector will be copied to the source SG + * vector. End of the vectors will be determined by sg_next() returning + * NULL. Returns number of bytes copied. + */ +int blk_copy_sg(struct scatterlist *dst_sg, + struct scatterlist *src_sg, size_t copy_len, + enum km_type d_km_type, enum km_type s_km_type) +{ + int res = 0; + size_t src_len, dst_len, src_offs, dst_offs; + struct page *src_page, *dst_page; + + if (copy_len == 0) + copy_len = 0x7FFFFFFF; /* copy all */ + + dst_page = sg_page(dst_sg); + dst_len = dst_sg->length; + dst_offs = dst_sg->offset; + + src_offs = 0; + do { + 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, s_km_type) + src_offs; + daddr = kmap_atomic(dst_page, d_km_type) + dst_offs; + + if ((src_offs == 0) && (dst_offs == 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_SIZE - src_offs); + 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, s_km_type); + kunmap_atomic(daddr, d_km_type); + + res += n; + copy_len -= n; + if (copy_len == 0) + goto out; + + if ((src_offs & ~PAGE_MASK) == 0) { + src_page = nth_page(src_page, 1); + src_offs = 0; + } + if ((dst_offs & ~PAGE_MASK) == 0) { + dst_page = nth_page(dst_page, 1); + dst_offs = 0; + } + + 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); + + src_sg = sg_next(src_sg); + } while (src_sg != NULL); + +out: + return res; +} +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) || !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */ + void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir) { struct scatterlist *src_sg, *dst_sg; - unsigned int src_sg_cnt, src_len, dst_len, src_offs, dst_offs; - struct page *src, *dst; - unsigned int s, d, to_copy; + unsigned int to_copy; + int atomic = scst_cmd_atomic(cmd); TRACE_ENTRY(); if (copy_dir == SCST_SG_COPY_FROM_TARGET) { if (cmd->data_direction != SCST_DATA_BIDI) { src_sg = cmd->tgt_sg; - src_sg_cnt = cmd->tgt_sg_cnt; dst_sg = cmd->sg; to_copy = cmd->bufflen; } else { TRACE_MEM("BIDI cmd %p", cmd); src_sg = cmd->tgt_in_sg; - src_sg_cnt = cmd->tgt_in_sg_cnt; dst_sg = cmd->in_sg; to_copy = cmd->in_bufflen; } } else { src_sg = cmd->sg; - src_sg_cnt = cmd->sg_cnt; dst_sg = cmd->tgt_sg; to_copy = cmd->resp_data_len; } - TRACE_MEM("cmd %p, copy_dir %d, src_sg %p, src_sg_cnt %d, dst_sg %p, " - "to_copy %d", cmd, copy_dir, src_sg, src_sg_cnt, dst_sg, - to_copy); + TRACE_MEM("cmd %p, copy_dir %d, src_sg %p, dst_sg %p, " + "to_copy %d", cmd, copy_dir, src_sg, dst_sg, to_copy); if (unlikely(src_sg == NULL) || unlikely(dst_sg == NULL)) { /* @@ -2405,68 +2505,8 @@ void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir) goto out; } - dst = sg_page(dst_sg); - dst_len = dst_sg->length; - dst_offs = dst_sg->offset; - - s = 0; - d = 0; - src_offs = 0; - while (s < src_sg_cnt) { - src = sg_page(&src_sg[s]); - src_len = src_sg[s].length; - src_offs += src_sg[s].offset; - - do { - unsigned int n; - - /* - * Himem pages are not allowed here, see the - * corresponding #warning in scst_main.c. Correct - * your target driver or dev handler to not alloc - * such pages! - */ - EXTRACHECKS_BUG_ON(PageHighMem(dst) || - PageHighMem(src)); - - TRACE_MEM("cmd %p, to_copy %d, src %p, src_len %d, " - "src_offs %d, dst %p, dst_len %d, dst_offs %d", - cmd, to_copy, src, src_len, src_offs, dst, - dst_len, dst_offs); - - if ((src_offs == 0) && (dst_offs == 0) && - (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE)) { - copy_page(page_address(dst), page_address(src)); - n = PAGE_SIZE; - } else { - n = min(PAGE_SIZE - dst_offs, - PAGE_SIZE - src_offs); - n = min(n, src_len); - n = min(n, dst_len); - memcpy(page_address(dst) + dst_offs, - page_address(src) + src_offs, n); - dst_offs -= min(n, dst_offs); - src_offs -= min(n, src_offs); - } - - TRACE_MEM("cmd %p, n %d, s %d", cmd, n, s); - - to_copy -= n; - if (to_copy <= 0) - goto out; - - src_len -= n; - dst_len -= n; - if (dst_len == 0) { - d++; - dst = sg_page(&dst_sg[d]); - dst_len = dst_sg[d].length; - dst_offs += dst_sg[d].offset; - } - } while (src_len > 0); - - s++; - } + blk_copy_sg(dst_sg, src_sg, to_copy, atomic ? KM_SOFTIRQ0 : KM_USER0, + atomic ? KM_SOFTIRQ1 : KM_USER1); out: TRACE_EXIT(); diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 44a93e1f8..2459824e7 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -40,12 +40,19 @@ details." #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) #if !defined(SCSI_EXEC_REQ_FIFO_DEFINED) && \ !defined(CONFIG_SCST_STRICT_SERIALIZING) #warning "Patch scst_exec_req_fifo- was not applied on\ your kernel and CONFIG_SCST_STRICT_SERIALIZING isn't defined.\ Pass-through dev handlers will not work." #endif +#else +#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED) +#warning "Patch scst_exec_req_fifo- was not applied on\ + your kernel. Pass-through dev handlers will not work." +#endif +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) #if !defined(SCST_IO_CONTEXT) @@ -918,6 +925,7 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type, if (res != 0) goto out_error; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) #if !defined(SCSI_EXEC_REQ_FIFO_DEFINED) && \ !defined(CONFIG_SCST_STRICT_SERIALIZING) if (dev_type->exec == NULL) { @@ -926,8 +934,19 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type, "scst_exec_req_fifo- or define " "CONFIG_SCST_STRICT_SERIALIZING", dev_type->name); res = -EINVAL; - goto out; + goto out_error; } +#endif +#else +#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED) + if (dev_type->exec == NULL) { + PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not " + "supported. Consider applying on your kernel patch " + "scst_exec_req_fifo-", dev_type->name); + res = -EINVAL; + goto out_error; + } +#endif #endif res = scst_suspend_activity(true); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index aa2cee5d7..a7578694a 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -340,23 +340,37 @@ static inline void scst_do_req(struct scsi_request *sreq, scsi_do_req_fifo(sreq, cmnd, buffer, bufflen, done, timeout, retries); #endif } -#else +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) static inline int scst_exec_req(struct scsi_device *sdev, const unsigned char *cmd, int cmd_len, int data_direction, - void *buffer, unsigned bufflen, int use_sg, int timeout, int retries, - void *privdata, void (*done)(void *, char *, int, int), gfp_t gfp) + struct scatterlist *sgl, unsigned bufflen, unsigned nents, + int timeout, int retries, void *privdata, + void (*done)(void *, char *, int, int), gfp_t gfp) { -#ifdef CONFIG_SCST_STRICT_SERIALIZING - return scsi_execute_async(sdev, cmd, cmd_len, data_direction, buffer, - bufflen, use_sg, timeout, retries, privdata, done, gfp); +#if defined(CONFIG_SCST_STRICT_SERIALIZING) + return scsi_execute_async(sdev, cmd, cmd_len, data_direction, (void*)sgl, + bufflen, nents, timeout, retries, privdata, done, gfp); #elif !defined(SCSI_EXEC_REQ_FIFO_DEFINED) - sBUG(); + WARN_ON(1); return -1; #else return scsi_execute_async_fifo(sdev, cmd, cmd_len, data_direction, - buffer, bufflen, use_sg, timeout, retries, privdata, done, gfp); + (void*)sgl, bufflen, nents, timeout, retries, privdata, done, gfp); #endif } +#else +#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED) +#define SCSI_ASYNC_EXEC_FLAG_HAS_TAIL_SPACE_FOR_PADDING 2 +static inline int scsi_execute_async(struct scsi_device *sdev, + const unsigned char *cmd, int cmd_len, int data_direction, + struct scatterlist *sgl, int nents, int timeout, int retries, + void *privdata, void (*done)(void *, char *, int, int), + gfp_t gfp, int flags) +{ + WARN_ON(1); + return -1; +} +#endif #endif int scst_alloc_space(struct scst_cmd *cmd); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 05105ed7c..63c5fd560 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -2059,16 +2059,25 @@ static int scst_do_real_exec(struct scst_cmd *cmd) cmd->scsi_req->sr_bufflen, scst_cmd_done, cmd->timeout, cmd->retries); #else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) rc = scst_exec_req(dev->scsi_dev, cmd->cdb, cmd->cdb_len, cmd->data_direction, cmd->sg, cmd->bufflen, cmd->sg_cnt, cmd->timeout, cmd->retries, cmd, scst_cmd_done, atomic ? GFP_ATOMIC : GFP_KERNEL); +#else + rc = scsi_execute_async(dev->scsi_dev, cmd->cdb, cmd->cdb_len, + cmd->data_direction, cmd->sg, cmd->sg_cnt, + cmd->timeout, cmd->retries, cmd, scst_cmd_done, + atomic ? GFP_ATOMIC : GFP_KERNEL, + cmd->tgt_data_buf_alloced ? 0 : + SCSI_ASYNC_EXEC_FLAG_HAS_TAIL_SPACE_FOR_PADDING); +#endif if (unlikely(rc != 0)) { if (atomic) { res = SCST_EXEC_NEED_THREAD; goto out_restore; } else { - PRINT_ERROR("scst_exec_req() failed: %d", res); + PRINT_ERROR("scst_exec_req() failed: %x", rc); goto out_error; } } diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 09c6a2ae1..4e9391708 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -136,6 +136,7 @@ struct scst_local_host_info { struct scst_tgt *target; struct scst_session *session[SCST_LOCAL_MAX_TARGETS]; struct device dev; + char init_name[20]; }; #define to_scst_lcl_host(d) \ @@ -692,7 +693,13 @@ static int scst_local_add_adapter(void) scst_lcl_host->dev.bus = &scst_fake_lld_bus; scst_lcl_host->dev.parent = &scst_fake_primary; scst_lcl_host->dev.release = &scst_local_release_adapter; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) sprintf(scst_lcl_host->dev.bus_id, "scst_adp_%d", scst_local_add_host); +#else + snprintf(scst_lcl_host->init_name, sizeof(scst_lcl_host->init_name), + "scst_adp_%d", scst_local_add_host); + scst_lcl_host->dev.init_name = scst_lcl_host->init_name; +#endif error = device_register(&scst_lcl_host->dev); if (error) @@ -780,7 +787,11 @@ static void scst_fake_0_release(struct device *dev) } static struct device scst_fake_primary = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) .bus_id = "scst_fake_0", +#else + .init_name = "scst_fake_0", +#endif .release = scst_fake_0_release, };