1/* 2 * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. 3 * Portions based on net/core/datagram.c and copyrighted by their authors. 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the Free 7 * Software Foundation; either version 2 of the License, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along with 16 * this program; if not, write to the Free Software Foundation, Inc., 59 17 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 * 19 * The full GNU General Public License is included in this distribution in the 20 * file called COPYING. 21 */ 22 23/* 24 * This code allows the net stack to make use of a DMA engine for 25 * skb to iovec copies. 26 */ 27 28#include <linux/dmaengine.h> 29#include <linux/socket.h> 30#include <net/tcp.h> 31#include <net/netdma.h> 32 33#define NET_DMA_DEFAULT_COPYBREAK 4096 34 35int sysctl_tcp_dma_copybreak = NET_DMA_DEFAULT_COPYBREAK; 36EXPORT_SYMBOL(sysctl_tcp_dma_copybreak); 37 38/** 39 * dma_skb_copy_datagram_iovec - Copy a datagram to an iovec. 40 * @skb - buffer to copy 41 * @offset - offset in the buffer to start copying from 42 * @iovec - io vector to copy to 43 * @len - amount of data to copy from buffer to iovec 44 * @pinned_list - locked iovec buffer data 45 * 46 * Note: the iovec is modified during the copy. 47 */ 48int dma_skb_copy_datagram_iovec(struct dma_chan *chan, 49 struct sk_buff *skb, int offset, struct iovec *to, 50 size_t len, struct dma_pinned_list *pinned_list) 51{ 52 int start = skb_headlen(skb); 53 int i, copy = start - offset; 54 struct sk_buff *frag_iter; 55 dma_cookie_t cookie = 0; 56 57 /* Copy header. */ 58 if (copy > 0) { 59 if (copy > len) 60 copy = len; 61 cookie = dma_memcpy_to_iovec(chan, to, pinned_list, 62 skb->data + offset, copy); 63 if (cookie < 0) 64 goto fault; 65 len -= copy; 66 if (len == 0) 67 goto end; 68 offset += copy; 69 } 70 71 /* Copy paged appendix. Hmm... why does this look so complicated? */ 72 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 73 int end; 74 75 WARN_ON(start > offset + len); 76 77 end = start + skb_shinfo(skb)->frags[i].size; 78 copy = end - offset; 79 if (copy > 0) { 80 skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 81 struct page *page = frag->page; 82 83 if (copy > len) 84 copy = len; 85 86 cookie = dma_memcpy_pg_to_iovec(chan, to, pinned_list, page, 87 frag->page_offset + offset - start, copy); 88 if (cookie < 0) 89 goto fault; 90 len -= copy; 91 if (len == 0) 92 goto end; 93 offset += copy; 94 } 95 start = end; 96 } 97 98 skb_walk_frags(skb, frag_iter) { 99 int end; 100 101 WARN_ON(start > offset + len); 102 103 end = start + frag_iter->len; 104 copy = end - offset; 105 if (copy > 0) { 106 if (copy > len) 107 copy = len; 108 cookie = dma_skb_copy_datagram_iovec(chan, frag_iter, 109 offset - start, 110 to, copy, 111 pinned_list); 112 if (cookie < 0) 113 goto fault; 114 len -= copy; 115 if (len == 0) 116 goto end; 117 offset += copy; 118 } 119 start = end; 120 } 121 122end: 123 if (!len) { 124 skb->dma_cookie = cookie; 125 return cookie; 126 } 127 128fault: 129 return -EFAULT; 130} 131