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 <linux/rtnetlink.h> /* for BUG_TRAP */ 31#include <net/tcp.h> 32#include <net/netdma.h> 33 34#define NET_DMA_DEFAULT_COPYBREAK 4096 35 36int sysctl_tcp_dma_copybreak = NET_DMA_DEFAULT_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 dma_cookie_t cookie = 0; 55 56 /* Copy header. */ 57 if (copy > 0) { 58 if (copy > len) 59 copy = len; 60 cookie = dma_memcpy_to_iovec(chan, to, pinned_list, 61 skb->data + offset, copy); 62 if (cookie < 0) 63 goto fault; 64 len -= copy; 65 if (len == 0) 66 goto end; 67 offset += copy; 68 } 69 70 /* Copy paged appendix. Hmm... why does this look so complicated? */ 71 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 72 int end; 73 74 BUG_TRAP(start <= offset + len); 75 76 end = start + skb_shinfo(skb)->frags[i].size; 77 copy = end - offset; 78 if ((copy = end - offset) > 0) { 79 skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 80 struct page *page = frag->page; 81 82 if (copy > len) 83 copy = len; 84 85 cookie = dma_memcpy_pg_to_iovec(chan, to, pinned_list, page, 86 frag->page_offset + offset - start, copy); 87 if (cookie < 0) 88 goto fault; 89 len -= copy; 90 if (len == 0) 91 goto end; 92 offset += copy; 93 } 94 start = end; 95 } 96 97 if (skb_shinfo(skb)->frag_list) { 98 struct sk_buff *list = skb_shinfo(skb)->frag_list; 99 100 for (; list; list = list->next) { 101 int end; 102 103 BUG_TRAP(start <= offset + len); 104 105 end = start + list->len; 106 copy = end - offset; 107 if (copy > 0) { 108 if (copy > len) 109 copy = len; 110 cookie = dma_skb_copy_datagram_iovec(chan, list, 111 offset - start, to, copy, 112 pinned_list); 113 if (cookie < 0) 114 goto fault; 115 len -= copy; 116 if (len == 0) 117 goto end; 118 offset += copy; 119 } 120 start = end; 121 } 122 } 123 124end: 125 if (!len) { 126 skb->dma_cookie = cookie; 127 return cookie; 128 } 129 130fault: 131 return -EFAULT; 132} 133