--- /dev/null
+diff -upr linux-2.6.18/include/linux/mm.h linux-2.6.18/include/linux/mm.h
+--- linux-2.6.18/include/linux/mm.h 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/include/linux/mm.h 2007-08-07 19:35:51.000000000 +0400
+@@ -277,6 +277,15 @@ struct page {
+ void *virtual; /* Kernel virtual address (NULL if
+ not kmapped, ie. highmem) */
+ #endif /* WANT_PAGE_VIRTUAL */
++ /*
++ * Used to implement support for notification on zero-copy TCP transfer
++ * completion. Not good to have this field here, it's better to have
++ * it in struct sk_buff, but it would make the code much more
++ * complicated and fragile, if maintained as a separate patch, since all
++ * skb then would have to contain only pages with the same value in this
++ * field.
++ */
++ void *net_priv;
+ };
+
+ #define page_private(page) ((page)->private)
+diff -upr linux-2.6.18/include/linux/net.h linux-2.6.18/include/linux/net.h
+--- linux-2.6.18/include/linux/net.h 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/include/linux/net.h 2007-08-29 18:28:21.000000000 +0400
+@@ -56,6 +56,7 @@ typedef enum {
+
+ #ifdef __KERNEL__
+ #include <linux/stringify.h>
++#include <linux/mm.h>
+
+ #define SOCK_ASYNC_NOSPACE 0
+ #define SOCK_ASYNC_WAITDATA 1
+@@ -324,5 +325,30 @@ extern int net_msg_cost;
+ extern int net_msg_burst;
+ #endif
+
++/* Support for notification on zero-copy TCP transfer completion */
++#define CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
++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);
++
++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);
++}
++
+ #endif /* __KERNEL__ */
+ #endif /* _LINUX_NET_H */
+diff -upr linux-2.6.18/net/core/skbuff.c linux-2.6.18/net/core/skbuff.c
+--- linux-2.6.18/net/core/skbuff.c 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/net/core/skbuff.c 2007-08-07 19:35:51.000000000 +0400
+@@ -324,7 +324,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)
+@@ -666,7 +666,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;
+ }
+@@ -720,7 +720,7 @@ int pskb_expand_head(struct sk_buff *skb
+ memcpy(data + size, skb->end, 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);
+@@ -902,7 +902,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);
+@@ -1071,7 +1071,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];
+@@ -1653,7 +1653,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;
+@@ -2021,7 +2021,7 @@ struct sk_buff *skb_segment(struct sk_bu
+ BUG_ON(i >= nfrags);
+
+ *frag = skb_shinfo(skb)->frags[i];
+- get_page(frag->page);
++ net_get_page(frag->page);
+ size = frag->size;
+
+ if (pos < offset) {
+diff -upr linux-2.6.18/net/core/utils.c linux-2.6.18/net/core/utils.c
+--- linux-2.6.18/net/core/utils.c 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/net/core/utils.c 2007-08-23 19:49:40.000000000 +0400
+@@ -24,11 +24,15 @@
+ #include <linux/random.h>
+ #include <linux/percpu.h>
+ #include <linux/init.h>
++#include <linux/skbuff.h>
+
+ #include <asm/byteorder.h>
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
+
++net_get_page_callback_t net_get_page_callback __read_mostly;
++net_put_page_callback_t net_put_page_callback __read_mostly;
++
+ /*
+ This is a maximally equidistributed combined Tausworthe generator
+ based on code from GNU Scientific Library 1.5 (30 Jun 2004)
+@@ -203,3 +203,32 @@ __be32 in_aton(const char *str)
+ }
+
+ EXPORT_SYMBOL(in_aton);
++
++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);
++
++EXPORT_SYMBOL(net_get_page_callback);
++EXPORT_SYMBOL(net_put_page_callback);
+diff -upr linux-2.6.18/net/ipv4/ip_output.c linux-2.6.18/net/ipv4/ip_output.c
+--- linux-2.6.18/net/ipv4/ip_output.c 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/net/ipv4/ip_output.c 2007-08-07 19:37:24.000000000 +0400
+@@ -1006,7 +1006,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];
+ }
+@@ -1166,7 +1166,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 -upr linux-2.6.18/net/ipv4/tcp.c linux-2.6.18/net/ipv4/tcp.c
+--- linux-2.6.18/net/ipv4/tcp.c 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/net/ipv4/tcp.c 2007-08-07 19:35:51.000000000 +0400
+@@ -560,7 +560,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);
+ }
+
+@@ -763,7 +763,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;
+ }
+@@ -804,9 +804,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 -upr linux-2.6.18/net/ipv4/tcp_output.c linux-2.6.18/net/ipv4/tcp_output.c
+--- linux-2.6.18/net/ipv4/tcp_output.c 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/net/ipv4/tcp_output.c 2007-08-07 19:35:51.000000000 +0400
+@@ -659,7 +659,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 -upr linux-2.6.18/net/ipv6/ip6_output.c linux-2.6.18/net/ipv6/ip6_output.c
+--- linux-2.6.18/net/ipv6/ip6_output.c 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/net/ipv6/ip6_output.c 2007-08-07 19:35:51.000000000 +0400
+@@ -1212,7 +1212,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];
+ }
--- /dev/null
+diff -upr linux-2.6.18/drivers/scsi/scsi_lib.c linux-2.6.18/drivers/scsi/scsi_lib.c
+--- linux-2.6.18/drivers/scsi/scsi_lib.c 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/drivers/scsi/scsi_lib.c 2007-07-04 21:15:32.000000000 +0400
+@@ -367,7 +367,7 @@ free_bios:
+ }
+
+ /**
+- * scsi_execute_async - insert request
++ * __scsi_execute_async - insert request
+ * @sdev: scsi device
+ * @cmd: scsi command
+ * @cmd_len: length of scsi cdb
+@@ -378,11 +378,14 @@ free_bios:
+ * @timeout: request timeout in seconds
+ * @retries: number of times to retry request
+ * @flags: or into request flags
++ * @at_head: insert request at head or tail of queue
+ **/
+-int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd,
++static inline int __scsi_execute_async(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)
++ void (*done)(void *, char *, int, int), gfp_t gfp,
++ int at_head)
+ {
+ struct request *req;
+ struct scsi_io_context *sioc;
+@@ -418,7 +421,7 @@ int scsi_execute_async(struct scsi_devic
+ sioc->data = privdata;
+ sioc->done = done;
+
+- blk_execute_rq_nowait(req->q, NULL, req, 1, scsi_end_async);
++ blk_execute_rq_nowait(req->q, NULL, req, at_head, scsi_end_async);
+ return 0;
+
+ free_req:
+@@ -427,8 +430,53 @@ free_sense:
+ kfree(sioc);
+ return DRIVER_ERROR << 24;
+ }
++
++/**
++ * scsi_execute_async - insert request
++ * @sdev: scsi device
++ * @cmd: scsi command
++ * @cmd_len: length of scsi cdb
++ * @data_direction: data direction
++ * @buffer: data buffer (this can be a kernel buffer or scatterlist)
++ * @bufflen: len of buffer
++ * @use_sg: if buffer is a scatterlist this is the number of elements
++ * @timeout: request timeout in seconds
++ * @retries: number of times to retry request
++ * @flags: or into request flags
++ **/
++int scsi_execute_async(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)
++{
++ return __scsi_execute_async(sdev, cmd, cmd_len, data_direction, buffer,
++ bufflen, use_sg, timeout, retries, privdata, done, gfp, 1);
++}
+ EXPORT_SYMBOL_GPL(scsi_execute_async);
+
++/**
++ * scsi_execute_async_fifo - insert request at tail, in FIFO order
++ * @sdev: scsi device
++ * @cmd: scsi command
++ * @cmd_len: length of scsi cdb
++ * @data_direction: data direction
++ * @buffer: data buffer (this can be a kernel buffer or scatterlist)
++ * @bufflen: len of buffer
++ * @use_sg: if buffer is a scatterlist this is the number of elements
++ * @timeout: request timeout in seconds
++ * @retries: number of times to retry request
++ * @flags: or into request flags
++ **/
++int scsi_execute_async_fifo(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)
++{
++ return __scsi_execute_async(sdev, cmd, cmd_len, data_direction, buffer,
++ bufflen, use_sg, timeout, retries, privdata, done, gfp, 0);
++}
++EXPORT_SYMBOL_GPL(scsi_execute_async_fifo);
++
+ /*
+ * Function: scsi_init_cmd_errh()
+ *
+diff -upr linux-2.6.18/include/scsi/scsi_device.h linux-2.6.18/include/scsi/scsi_device.h
+--- linux-2.6.18/include/scsi/scsi_device.h 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18/include/scsi/scsi_device.h 2007-07-04 21:15:32.000000000 +0400
+@@ -335,6 +335,13 @@ extern int scsi_execute_async(struct scs
+ int timeout, int retries, void *privdata,
+ void (*done)(void *, char *, int, int),
+ gfp_t gfp);
++#define SCSI_EXEC_REQ_FIFO_DEFINED
++extern int scsi_execute_async_fifo(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);
+
+ static inline void scsi_device_reprobe(struct scsi_device *sdev)
+ {