2006-05-24 04:50:37 +04:00
/*
* Copyright ( c ) 2004 - 2006 Intel Corporation . All rights reserved .
* Portions based on net / core / datagram . c and copyrighted by their authors .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . , 59
* Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING .
*/
/*
* This code allows the net stack to make use of a DMA engine for
* skb to iovec copies .
*/
# include <linux/dmaengine.h>
# include <linux/socket.h>
# include <net/tcp.h>
2006-07-22 01:49:49 +04:00
# include <net/netdma.h>
2006-05-24 04:50:37 +04:00
2006-05-24 05:02:55 +04:00
# define NET_DMA_DEFAULT_COPYBREAK 4096
int sysctl_tcp_dma_copybreak = NET_DMA_DEFAULT_COPYBREAK ;
2008-07-23 04:30:57 +04:00
EXPORT_SYMBOL ( sysctl_tcp_dma_copybreak ) ;
2006-05-24 05:02:55 +04:00
2006-05-24 04:50:37 +04:00
/**
* dma_skb_copy_datagram_iovec - Copy a datagram to an iovec .
* @ skb - buffer to copy
* @ offset - offset in the buffer to start copying from
* @ iovec - io vector to copy to
* @ len - amount of data to copy from buffer to iovec
* @ pinned_list - locked iovec buffer data
*
* Note : the iovec is modified during the copy .
*/
int dma_skb_copy_datagram_iovec ( struct dma_chan * chan ,
struct sk_buff * skb , int offset , struct iovec * to ,
size_t len , struct dma_pinned_list * pinned_list )
{
2007-04-28 02:21:23 +04:00
int start = skb_headlen ( skb ) ;
int i , copy = start - offset ;
2006-05-24 04:50:37 +04:00
dma_cookie_t cookie = 0 ;
/* Copy header. */
if ( copy > 0 ) {
if ( copy > len )
copy = len ;
cookie = dma_memcpy_to_iovec ( chan , to , pinned_list ,
2007-02-09 17:24:36 +03:00
skb - > data + offset , copy ) ;
2006-05-24 04:50:37 +04:00
if ( cookie < 0 )
goto fault ;
len - = copy ;
if ( len = = 0 )
goto end ;
offset + = copy ;
}
/* Copy paged appendix. Hmm... why does this look so complicated? */
for ( i = 0 ; i < skb_shinfo ( skb ) - > nr_frags ; i + + ) {
2007-04-28 02:21:23 +04:00
int end ;
2006-05-24 04:50:37 +04:00
2008-07-26 08:43:18 +04:00
WARN_ON ( start > offset + len ) ;
2007-04-28 02:21:23 +04:00
end = start + skb_shinfo ( skb ) - > frags [ i ] . size ;
2006-05-24 04:50:37 +04:00
copy = end - offset ;
2008-06-04 03:07:45 +04:00
if ( copy > 0 ) {
2006-05-24 04:50:37 +04:00
skb_frag_t * frag = & skb_shinfo ( skb ) - > frags [ i ] ;
struct page * page = frag - > page ;
if ( copy > len )
copy = len ;
2007-04-28 02:21:23 +04:00
cookie = dma_memcpy_pg_to_iovec ( chan , to , pinned_list , page ,
frag - > page_offset + offset - start , copy ) ;
2006-05-24 04:50:37 +04:00
if ( cookie < 0 )
goto fault ;
len - = copy ;
if ( len = = 0 )
goto end ;
offset + = copy ;
}
2007-04-28 02:21:23 +04:00
start = end ;
2006-05-24 04:50:37 +04:00
}
if ( skb_shinfo ( skb ) - > frag_list ) {
struct sk_buff * list = skb_shinfo ( skb ) - > frag_list ;
for ( ; list ; list = list - > next ) {
2007-04-28 02:21:23 +04:00
int end ;
2008-07-26 08:43:18 +04:00
WARN_ON ( start > offset + len ) ;
2006-05-24 04:50:37 +04:00
2007-04-28 02:21:23 +04:00
end = start + list - > len ;
2006-05-24 04:50:37 +04:00
copy = end - offset ;
if ( copy > 0 ) {
if ( copy > len )
copy = len ;
cookie = dma_skb_copy_datagram_iovec ( chan , list ,
2007-04-28 02:21:23 +04:00
offset - start , to , copy ,
pinned_list ) ;
2006-05-24 04:50:37 +04:00
if ( cookie < 0 )
goto fault ;
len - = copy ;
if ( len = = 0 )
goto end ;
offset + = copy ;
}
2007-04-28 02:21:23 +04:00
start = end ;
2006-05-24 04:50:37 +04:00
}
}
end :
if ( ! len ) {
skb - > dma_cookie = cookie ;
return cookie ;
}
fault :
2007-02-09 17:24:36 +03:00
return - EFAULT ;
2006-05-24 04:50:37 +04:00
}