Beispiel #1
0
static int skb_remove_foreign_references(struct sk_buff *skb)
{
	struct page *page;
	unsigned long pfn;
	int i, off;
	char *vaddr;

	BUG_ON(skb_shinfo(skb)->frag_list);

	if (skb_cloned(skb) &&
	    unlikely(pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
		return 0;

	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		pfn = page_to_pfn(skb_shinfo(skb)->frags[i].page);
		if (!is_foreign(pfn))
			continue;
		
		page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
		if (unlikely(!page))
			return 0;

		vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
		off = skb_shinfo(skb)->frags[i].page_offset;
		memcpy(page_address(page) + off,
		       vaddr + off,
		       skb_shinfo(skb)->frags[i].size);
		kunmap_skb_frag(vaddr);

		put_page(skb_shinfo(skb)->frags[i].page);
		skb_shinfo(skb)->frags[i].page = page;
	}

	return 1;
}
/** Copy some data bits from a kernel buffer to an skb.
 * Derived in the obvious way from skb_copy_bits().
 */
int skb_put_bits(const struct sk_buff *skb, int offset, void *src, int len)
{
    int i, copy;
    int start = skb->len - skb->data_len;

    if (offset > (int)skb->len-len)
        goto fault;

    /* Copy header. */
    if ((copy = start-offset) > 0) {
        if (copy > len)
            copy = len;
        memcpy(skb->data + offset, src, copy);
        if ((len -= copy) == 0)
            return 0;
        offset += copy;
        src += copy;
    }

#ifdef __KERNEL__
    for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
        int end;

        BUG_TRAP(start <= offset+len);

        end = start + skb_shinfo(skb)->frags[i].size;
        if ((copy = end-offset) > 0) {
            u8 *vaddr;

            if (copy > len)
                copy = len;

            vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
            memcpy(vaddr + skb_shinfo(skb)->frags[i].page_offset + offset - start,
                   src,
                   copy);
            kunmap_skb_frag(vaddr);

            if ((len -= copy) == 0)
                return 0;
            offset += copy;
            src += copy;
        }
        start = end;
    }

    if (skb_shinfo(skb)->frag_list) {
        struct sk_buff *list;
        
        for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
            int end;
            
            BUG_TRAP(start <= offset+len);
            
            end = start + list->len;
            if ((copy = end-offset) > 0) {
                if (copy > len)
                    copy = len;
                if (skb_put_bits(list, offset-start, src, copy))
                    goto fault;
                if ((len -= copy) == 0)
                    return 0;
                offset += copy;
                src += copy;
            }
            start = end;
        }
    }
#else
    i=0;
#endif

    if (len == 0)
        return 0;

 fault:
    return -EFAULT;
}