mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
and optionally running in user-space. Signed-off-by: Mike Wray <mike.wray@hp.com>
531 lines
13 KiB
C
531 lines
13 KiB
C
/*
|
|
* Routines having to do with the 'struct sk_buff' memory handlers.
|
|
*
|
|
* Authors: Alan Cox <iiitac@pyr.swan.ac.uk>
|
|
* Florian La Roche <rzsfl@rz.uni-sb.de>
|
|
*
|
|
* Fixes:
|
|
* Alan Cox : Fixed the worst of the load
|
|
* balancer bugs.
|
|
* Dave Platt : Interrupt stacking fix.
|
|
* Richard Kooijman : Timestamp fixes.
|
|
* Alan Cox : Changed buffer format.
|
|
* Alan Cox : destructor hook for AF_UNIX etc.
|
|
* Linus Torvalds : Better skb_clone.
|
|
* Alan Cox : Added skb_copy.
|
|
* Alan Cox : Added all the changed routines Linus
|
|
* only put in the headers
|
|
* Ray VanTassle : Fixed --skb->lock in free
|
|
* Alan Cox : skb_copy copy arp field
|
|
* Andi Kleen : slabified it.
|
|
* Robert Olsson : Removed skb_head_pool
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "allocate.h"
|
|
#include "debug.h"
|
|
#include "skbuff.h"
|
|
|
|
#define SKB_DATA_ALIGN(size) ((((size) + 7) >> 3) << 3)
|
|
|
|
/**
|
|
* skb_over_panic - private function
|
|
* @skb: buffer
|
|
* @sz: size
|
|
* @here: address
|
|
*
|
|
* Out of line support code for skb_put(). Not user callable.
|
|
*/
|
|
void skb_over_panic(struct sk_buff *skb, int sz, void *here)
|
|
{
|
|
eprintf("skput:over: %p:%d put:%d\n", here, skb->len, sz);
|
|
BUG();
|
|
}
|
|
|
|
/**
|
|
* skb_under_panic - private function
|
|
* @skb: buffer
|
|
* @sz: size
|
|
* @here: address
|
|
*
|
|
* Out of line support code for skb_push(). Not user callable.
|
|
*/
|
|
|
|
void skb_under_panic(struct sk_buff *skb, int sz, void *here)
|
|
{
|
|
eprintf("skput:under: %p:%d put:%d\n", here, skb->len, sz);
|
|
BUG();
|
|
}
|
|
|
|
/**
|
|
* alloc_skb - allocate a network buffer
|
|
* @size: size to allocate
|
|
* @gfp_mask: allocation mask
|
|
*
|
|
* Allocate a new &sk_buff. The returned buffer has no headroom and a
|
|
* tail room of size bytes. The object has a reference count of one.
|
|
* The return is the buffer. On a failure the return is %NULL.
|
|
*/
|
|
struct sk_buff *alloc_skb(unsigned int size, int gfp_mask)
|
|
{
|
|
struct sk_buff *skb;
|
|
u8 *data;
|
|
|
|
/* Get the HEAD */
|
|
skb = ALLOCATE(struct sk_buff);
|
|
if (!skb)
|
|
goto out;
|
|
|
|
/* Get the DATA. Size must match skb_add_mtu(). */
|
|
size = SKB_DATA_ALIGN(size);
|
|
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
|
|
if (!data)
|
|
goto nodata;
|
|
|
|
memset(skb, 0, offsetof(struct sk_buff, truesize));
|
|
skb->truesize = size + sizeof(struct sk_buff);
|
|
atomic_set(&skb->users, 1);
|
|
skb->head = data;
|
|
skb->data = data;
|
|
skb->tail = data;
|
|
skb->end = data + size;
|
|
skb->list = NULL;
|
|
|
|
atomic_set(&(skb_shinfo(skb)->dataref), 1);
|
|
skb_shinfo(skb)->nr_frags = 0;
|
|
skb_shinfo(skb)->tso_size = 0;
|
|
skb_shinfo(skb)->tso_segs = 0;
|
|
skb_shinfo(skb)->frag_list = NULL;
|
|
out:
|
|
return skb;
|
|
nodata:
|
|
kfree(skb);
|
|
skb = NULL;
|
|
goto out;
|
|
}
|
|
|
|
|
|
void skb_release_data(struct sk_buff *skb)
|
|
{
|
|
kfree(skb->head);
|
|
}
|
|
|
|
/*
|
|
* Free an skbuff by memory without cleaning the state.
|
|
*/
|
|
void kfree_skbmem(struct sk_buff *skb)
|
|
{
|
|
skb_release_data(skb);
|
|
kfree(skb);
|
|
}
|
|
|
|
/**
|
|
* __kfree_skb - private function
|
|
* @skb: buffer
|
|
*
|
|
* Free an sk_buff. Release anything attached to the buffer.
|
|
* Clean the state. This is an internal helper function. Users should
|
|
* always call kfree_skb
|
|
*/
|
|
|
|
void __kfree_skb(struct sk_buff *skb)
|
|
{
|
|
if (skb->list) {
|
|
wprintf("Warning: kfree_skb passed an skb still "
|
|
"on a list.\n");
|
|
//BUG();
|
|
}
|
|
|
|
if(skb->destructor) {
|
|
skb->destructor(skb);
|
|
}
|
|
kfree_skbmem(skb);
|
|
}
|
|
|
|
|
|
/**
|
|
* skb_clone - duplicate an sk_buff
|
|
* @skb: buffer to clone
|
|
* @gfp_mask: allocation priority
|
|
*
|
|
* Duplicate an &sk_buff. The new one is not owned by a socket. Both
|
|
* copies share the same packet data but not structure. The new
|
|
* buffer has a reference count of 1. If the allocation fails the
|
|
* function returns %NULL otherwise the new buffer is returned.
|
|
*
|
|
* If this function is called from an interrupt gfp_mask() must be
|
|
* %GFP_ATOMIC.
|
|
*/
|
|
|
|
struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
|
|
{
|
|
return pskb_copy(skb, gfp_mask);
|
|
}
|
|
|
|
static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
|
|
{
|
|
/*
|
|
* Shift between the two data areas in bytes
|
|
*/
|
|
unsigned long offset = new->data - old->data;
|
|
|
|
new->list = NULL;
|
|
new->protocol = old->protocol;
|
|
new->h.raw = old->h.raw + offset;
|
|
new->nh.raw = old->nh.raw + offset;
|
|
new->mac.raw = old->mac.raw + offset;
|
|
new->pkt_type = old->pkt_type;
|
|
new->destructor = NULL;
|
|
atomic_set(&new->users, 1);
|
|
}
|
|
|
|
|
|
/**
|
|
* pskb_expand_head - reallocate header of &sk_buff
|
|
* @skb: buffer to reallocate
|
|
* @nhead: room to add at head
|
|
* @ntail: room to add at tail
|
|
* @gfp_mask: allocation priority
|
|
*
|
|
* Expands (or creates identical copy, if &nhead and &ntail are zero)
|
|
* header of skb. &sk_buff itself is not changed. &sk_buff MUST have
|
|
* reference count of 1. Returns zero in the case of success or error,
|
|
* if expansion failed. In the last case, &sk_buff is not changed.
|
|
*
|
|
* All the pointers pointing into skb header may change and must be
|
|
* reloaded after call to this function.
|
|
*/
|
|
|
|
int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask)
|
|
{
|
|
u8 *data;
|
|
int size = nhead + (skb->end - skb->head) + ntail;
|
|
long off;
|
|
|
|
if (skb_shared(skb))
|
|
BUG();
|
|
|
|
size = SKB_DATA_ALIGN(size);
|
|
|
|
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
|
|
if (!data)
|
|
goto nodata;
|
|
|
|
/* Copy only real data... and, alas, header. This should be
|
|
* optimized for the cases when header is void. */
|
|
memcpy(data + nhead, skb->head, skb->tail - skb->head);
|
|
memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
|
|
|
|
skb_release_data(skb);
|
|
|
|
off = (data + nhead) - skb->head;
|
|
|
|
skb->head = data;
|
|
skb->end = data + size;
|
|
skb->data += off;
|
|
skb->tail += off;
|
|
skb->mac.raw += off;
|
|
skb->h.raw += off;
|
|
skb->nh.raw += off;
|
|
return 0;
|
|
|
|
nodata:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)
|
|
{
|
|
/*
|
|
* Allocate the copy buffer
|
|
*/
|
|
struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask);
|
|
|
|
if (!n)
|
|
goto out;
|
|
|
|
/* Set the data pointer */
|
|
skb_reserve(n, skb->data - skb->head);
|
|
/* Set the tail pointer and length */
|
|
skb_put(n, skb_headlen(skb));
|
|
/* Copy the bytes */
|
|
memcpy(n->data, skb->data, n->len);
|
|
|
|
n->data_len = skb->data_len;
|
|
n->len = skb->len;
|
|
|
|
copy_skb_header(n, skb);
|
|
out:
|
|
return n;
|
|
}
|
|
|
|
/* Make private copy of skb with writable head and some headroom */
|
|
|
|
struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
|
|
{
|
|
struct sk_buff *skb2;
|
|
int delta = headroom - skb_headroom(skb);
|
|
|
|
if (delta <= 0)
|
|
skb2 = pskb_copy(skb, GFP_ATOMIC);
|
|
else {
|
|
skb2 = skb_copy_expand(skb, headroom, 0, GFP_ATOMIC);
|
|
}
|
|
return skb2;
|
|
}
|
|
|
|
|
|
/**
|
|
* skb_copy_expand - copy and expand sk_buff
|
|
* @skb: buffer to copy
|
|
* @newheadroom: new free bytes at head
|
|
* @newtailroom: new free bytes at tail
|
|
* @gfp_mask: allocation priority
|
|
*
|
|
* Make a copy of both an &sk_buff and its data and while doing so
|
|
* allocate additional space.
|
|
*
|
|
* This is used when the caller wishes to modify the data and needs a
|
|
* private copy of the data to alter as well as more space for new fields.
|
|
* Returns %NULL on failure or the pointer to the buffer
|
|
* on success. The returned buffer has a reference count of 1.
|
|
*
|
|
* You must pass %GFP_ATOMIC as the allocation priority if this function
|
|
* is called from an interrupt.
|
|
*
|
|
* BUG ALERT: ip_summed is not copied. Why does this work? Is it used
|
|
* only by netfilter in the cases when checksum is recalculated? --ANK
|
|
*/
|
|
struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
|
|
int newheadroom, int newtailroom, int gfp_mask)
|
|
{
|
|
/*
|
|
* Allocate the copy buffer
|
|
*/
|
|
struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom,
|
|
gfp_mask);
|
|
int head_copy_len, head_copy_off;
|
|
|
|
if (!n)
|
|
return NULL;
|
|
|
|
skb_reserve(n, newheadroom);
|
|
|
|
/* Set the tail pointer and length */
|
|
skb_put(n, skb->len);
|
|
|
|
head_copy_len = skb_headroom(skb);
|
|
head_copy_off = 0;
|
|
if (newheadroom <= head_copy_len)
|
|
head_copy_len = newheadroom;
|
|
else
|
|
head_copy_off = newheadroom - head_copy_len;
|
|
|
|
/* Copy the linear header and data. */
|
|
if (skb_copy_bits(skb, -head_copy_len, n->head + head_copy_off,
|
|
skb->len + head_copy_len))
|
|
BUG();
|
|
|
|
copy_skb_header(n, skb);
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
/* Copy some data bits from skb to kernel buffer. */
|
|
|
|
int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
|
|
{
|
|
int copy;
|
|
int start = skb_headlen(skb);
|
|
|
|
if (offset > (int)skb->len - len)
|
|
goto fault;
|
|
|
|
/* Copy header. */
|
|
if ((copy = start - offset) > 0) {
|
|
if (copy > len)
|
|
copy = len;
|
|
memcpy(to, skb->data + offset, copy);
|
|
if ((len -= copy) == 0)
|
|
return 0;
|
|
offset += copy;
|
|
to += copy;
|
|
}
|
|
|
|
if (!len)
|
|
return 0;
|
|
|
|
fault:
|
|
return -EFAULT;
|
|
}
|
|
|
|
|
|
/**
|
|
* skb_dequeue - remove from the head of the queue
|
|
* @list: list to dequeue from
|
|
*
|
|
* Remove the head of the list. The list lock is taken so the function
|
|
* may be used safely with other locking list functions. The head item is
|
|
* returned or %NULL if the list is empty.
|
|
*/
|
|
|
|
struct sk_buff *skb_dequeue(struct sk_buff_head *list)
|
|
{
|
|
unsigned long flags;
|
|
struct sk_buff *result;
|
|
|
|
spin_lock_irqsave(&list->lock, flags);
|
|
result = __skb_dequeue(list);
|
|
spin_unlock_irqrestore(&list->lock, flags);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* skb_dequeue_tail - remove from the tail of the queue
|
|
* @list: list to dequeue from
|
|
*
|
|
* Remove the tail of the list. The list lock is taken so the function
|
|
* may be used safely with other locking list functions. The tail item is
|
|
* returned or %NULL if the list is empty.
|
|
*/
|
|
struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
|
|
{
|
|
unsigned long flags;
|
|
struct sk_buff *result;
|
|
|
|
spin_lock_irqsave(&list->lock, flags);
|
|
result = __skb_dequeue_tail(list);
|
|
spin_unlock_irqrestore(&list->lock, flags);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* skb_queue_purge - empty a list
|
|
* @list: list to empty
|
|
*
|
|
* Delete all buffers on an &sk_buff list. Each buffer is removed from
|
|
* the list and one reference dropped. This function takes the list
|
|
* lock and is atomic with respect to other list locking functions.
|
|
*/
|
|
void skb_queue_purge(struct sk_buff_head *list)
|
|
{
|
|
struct sk_buff *skb;
|
|
while ((skb = skb_dequeue(list)) != NULL)
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
/**
|
|
* skb_queue_head - queue a buffer at the list head
|
|
* @list: list to use
|
|
* @newsk: buffer to queue
|
|
*
|
|
* Queue a buffer at the start of the list. This function takes the
|
|
* list lock and can be used safely with other locking &sk_buff functions
|
|
* safely.
|
|
*
|
|
* A buffer cannot be placed on two lists at the same time.
|
|
*/
|
|
void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&list->lock, flags);
|
|
__skb_queue_head(list, newsk);
|
|
spin_unlock_irqrestore(&list->lock, flags);
|
|
}
|
|
|
|
/**
|
|
* skb_queue_tail - queue a buffer at the list tail
|
|
* @list: list to use
|
|
* @newsk: buffer to queue
|
|
*
|
|
* Queue a buffer at the tail of the list. This function takes the
|
|
* list lock and can be used safely with other locking &sk_buff functions
|
|
* safely.
|
|
*
|
|
* A buffer cannot be placed on two lists at the same time.
|
|
*/
|
|
void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&list->lock, flags);
|
|
__skb_queue_tail(list, newsk);
|
|
spin_unlock_irqrestore(&list->lock, flags);
|
|
}
|
|
/**
|
|
* skb_unlink - remove a buffer from a list
|
|
* @skb: buffer to remove
|
|
*
|
|
* Place a packet after a given packet in a list. The list locks are taken
|
|
* and this function is atomic with respect to other list locked calls
|
|
*
|
|
* Works even without knowing the list it is sitting on, which can be
|
|
* handy at times. It also means that THE LIST MUST EXIST when you
|
|
* unlink. Thus a list must have its contents unlinked before it is
|
|
* destroyed.
|
|
*/
|
|
void skb_unlink(struct sk_buff *skb)
|
|
{
|
|
struct sk_buff_head *list = skb->list;
|
|
|
|
if (list) {
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&list->lock, flags);
|
|
if (skb->list == list)
|
|
__skb_unlink(skb, skb->list);
|
|
spin_unlock_irqrestore(&list->lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* skb_append - append a buffer
|
|
* @old: buffer to insert after
|
|
* @newsk: buffer to insert
|
|
*
|
|
* Place a packet after a given packet in a list. The list locks are taken
|
|
* and this function is atomic with respect to other list locked calls.
|
|
* A buffer cannot be placed on two lists at the same time.
|
|
*/
|
|
|
|
void skb_append(struct sk_buff *old, struct sk_buff *newsk)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&old->list->lock, flags);
|
|
__skb_append(old, newsk);
|
|
spin_unlock_irqrestore(&old->list->lock, flags);
|
|
}
|
|
|
|
|
|
/**
|
|
* skb_insert - insert a buffer
|
|
* @old: buffer to insert before
|
|
* @newsk: buffer to insert
|
|
*
|
|
* Place a packet before a given packet in a list. The list locks are taken
|
|
* and this function is atomic with respect to other list locked calls
|
|
* A buffer cannot be placed on two lists at the same time.
|
|
*/
|
|
|
|
void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&old->list->lock, flags);
|
|
__skb_insert(newsk, old->prev, old, old->list);
|
|
spin_unlock_irqrestore(&old->list->lock, flags);
|
|
}
|
|
|