Linux Audio

Check our new training course

Loading...
  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/export.h>
 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;
 37EXPORT_SYMBOL(sysctl_tcp_dma_copybreak);
 38
 39/**
 40 *	dma_skb_copy_datagram_iovec - Copy a datagram to an iovec.
 41 *	@skb - buffer to copy
 42 *	@offset - offset in the buffer to start copying from
 43 *	@iovec - io vector to copy to
 44 *	@len - amount of data to copy from buffer to iovec
 45 *	@pinned_list - locked iovec buffer data
 46 *
 47 *	Note: the iovec is modified during the copy.
 48 */
 49int dma_skb_copy_datagram_iovec(struct dma_chan *chan,
 50			struct sk_buff *skb, int offset, struct iovec *to,
 51			size_t len, struct dma_pinned_list *pinned_list)
 52{
 53	int start = skb_headlen(skb);
 54	int i, copy = start - offset;
 55	struct sk_buff *frag_iter;
 56	dma_cookie_t cookie = 0;
 57
 58	/* Copy header. */
 59	if (copy > 0) {
 60		if (copy > len)
 61			copy = len;
 62		cookie = dma_memcpy_to_iovec(chan, to, pinned_list,
 63					    skb->data + offset, copy);
 64		if (cookie < 0)
 65			goto fault;
 66		len -= copy;
 67		if (len == 0)
 68			goto end;
 69		offset += copy;
 70	}
 71
 72	/* Copy paged appendix. Hmm... why does this look so complicated? */
 73	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 74		int end;
 75		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 76
 77		WARN_ON(start > offset + len);
 78
 79		end = start + skb_frag_size(frag);
 80		copy = end - offset;
 81		if (copy > 0) {
 82			struct page *page = skb_frag_page(frag);
 83
 84			if (copy > len)
 85				copy = len;
 86
 87			cookie = dma_memcpy_pg_to_iovec(chan, to, pinned_list, page,
 88					frag->page_offset + offset - start, copy);
 89			if (cookie < 0)
 90				goto fault;
 91			len -= copy;
 92			if (len == 0)
 93				goto end;
 94			offset += copy;
 95		}
 96		start = end;
 97	}
 98
 99	skb_walk_frags(skb, frag_iter) {
100		int end;
101
102		WARN_ON(start > offset + len);
103
104		end = start + frag_iter->len;
105		copy = end - offset;
106		if (copy > 0) {
107			if (copy > len)
108				copy = len;
109			cookie = dma_skb_copy_datagram_iovec(chan, frag_iter,
110							     offset - start,
111							     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
123end:
124	if (!len) {
125		skb->dma_cookie = cookie;
126		return cookie;
127	}
128
129fault:
130	return -EFAULT;
131}